@bndynet/vue-site 1.5.0 → 1.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +134 -20
- package/README.zh.md +112 -8
- package/bin/vue-site.mjs +4 -2
- package/dist/components/AppLayout.vue.d.ts +5 -1
- package/dist/components/LocaleSwitch.vue.d.ts +1 -0
- package/dist/components/NavGroup.vue.d.ts +9 -1
- package/dist/components/NavItem.vue.d.ts +2 -0
- package/dist/components/SideNav.vue.d.ts +12 -1
- package/dist/components/ThemeSwitch.vue.d.ts +1 -0
- package/dist/composables/useMobileViewport.d.ts +36 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.es.js +3133 -2724
- package/dist/style.css +1 -1
- package/dist/theme/presets.d.ts +5 -1
- package/dist/types.d.ts +20 -7
- package/package.json +4 -3
package/README.md
CHANGED
|
@@ -38,6 +38,7 @@ You can also create the configuration manually:
|
|
|
38
38
|
import { defineConfig } from '@bndynet/vue-site'
|
|
39
39
|
|
|
40
40
|
export default defineConfig({
|
|
41
|
+
name: 'My Project',
|
|
41
42
|
title: 'My Project',
|
|
42
43
|
favicon: '/favicon.ico',
|
|
43
44
|
nav: [
|
|
@@ -100,10 +101,11 @@ Add `"dev": "vue-site dev"` (or `vs dev`) in `package.json` scripts if you like.
|
|
|
100
101
|
|
|
101
102
|
| Property | Description |
|
|
102
103
|
|----------|-------------|
|
|
103
|
-
| `
|
|
104
|
+
| `name` | Brand name shown next to the logo (header + sidebar). `LocalizedString`. Optional — omitted or empty means no brand text is rendered |
|
|
105
|
+
| `title` | Browser tab title. `LocalizedString`. Optional — omitted or empty means the document title is left unchanged |
|
|
104
106
|
| `nav` | `NavItem[]` |
|
|
105
107
|
| `navPosition` | Optional navigation placement: `'top'` forces the top/tiered layout, `'sidebar'` forces the full nav tree into the sidebar. Omit to keep automatic behavior based on nav depth |
|
|
106
|
-
| `defaultPath` | Path the site opens at; `/` and unknown paths redirect here. Must match a registered route (a `nav` item's resolved path or a `pages` entry's `path`). Defaults to the first
|
|
108
|
+
| `defaultPath` | Path the site opens at; `/` and unknown paths redirect here. Must match a registered route (a `nav` item's resolved path or a `pages` entry's `path`). Defaults to the first registered page in navigation order, then the first visible standalone page |
|
|
107
109
|
| `favicon` | Browser tab icon URL. Use a public asset path, remote URL, or imported asset URL |
|
|
108
110
|
| `logo` | Logo URL or imported image |
|
|
109
111
|
| `theme` | See `ThemeConfig` below; set to `false` to disable theming (hides the switcher, forces a fixed `light` palette, no localStorage persistence) |
|
|
@@ -231,6 +233,20 @@ export default defineConfig({
|
|
|
231
233
|
})
|
|
232
234
|
```
|
|
233
235
|
|
|
236
|
+
### Responsive helpers
|
|
237
|
+
|
|
238
|
+
Wide content — such as an Element Plus `el-table` or a multi-column `el-form` — can overflow on
|
|
239
|
+
small screens. Wrap it in a `.site-responsive-scroll` container to give it a horizontal scrollbar
|
|
240
|
+
(with momentum scrolling on touch and a visible scrollbar hint) instead of overflowing the page:
|
|
241
|
+
|
|
242
|
+
```vue
|
|
243
|
+
<template>
|
|
244
|
+
<div class="site-responsive-scroll">
|
|
245
|
+
<el-table :data="rows">…</el-table>
|
|
246
|
+
</div>
|
|
247
|
+
</template>
|
|
248
|
+
```
|
|
249
|
+
|
|
234
250
|
### Icon registry
|
|
235
251
|
|
|
236
252
|
The CLI scans configured Lucide icon names in `nav`, `links`, `i18n.locales`, and `theme.extraThemes`
|
|
@@ -265,13 +281,84 @@ Built-in themes are `light`, `dark`, plus the always-on extras `sepia` and `ocea
|
|
|
265
281
|
| `palettes` | — | Partial overrides for built-in light/dark only |
|
|
266
282
|
| `extraThemes` | — | Extra themes: `id`, `label`, `icon`, optional `basedOn`, `palette`; reuse a built-in id (`sepia`/`ocean`) to override it. Import `builtinThemePalettes` for full defaults |
|
|
267
283
|
|
|
284
|
+
### Semantic color tokens
|
|
285
|
+
|
|
286
|
+
Every theme is driven by semantic `--color-*` CSS variables. Shared Element Plus variables are
|
|
287
|
+
mapped to them in `element-plus-theme.css`, so common controls and surfaces follow the active theme.
|
|
288
|
+
Some components also have component-specific variables; those may need additional mapping when
|
|
289
|
+
adding new Element Plus components. Built-in palettes define seed colors and derive related values
|
|
290
|
+
with `color-mix()` / `var()`, so `builtinThemePalettes` may contain CSS expressions rather than
|
|
291
|
+
literal colors.
|
|
292
|
+
|
|
293
|
+
| Token | Purpose |
|
|
294
|
+
|-------|---------|
|
|
295
|
+
| `--color-primary` | Brand / primary (`--el-color-primary` and its tints) |
|
|
296
|
+
| `--color-primary-contrast` | Text color on filled primary controls; defaults to the theme background |
|
|
297
|
+
| `--color-primary-hover` | Hover color for filled primary controls; derived from primary and text colors |
|
|
298
|
+
| `--color-success` | Success |
|
|
299
|
+
| `--color-warning` | Warning |
|
|
300
|
+
| `--color-danger` | Danger / error (`--el-color-error` reuses it) |
|
|
301
|
+
| `--color-info` | Info |
|
|
302
|
+
| `--color-surface-raised` | Elevated surfaces: popovers, dropdowns, dialogs, masks |
|
|
303
|
+
| `--color-overlay` | Modal / loading overlay |
|
|
304
|
+
|
|
305
|
+
Define just these base colors in a custom theme; the framework derives Element Plus's
|
|
306
|
+
`light-3/5/7/8/9` and `dark-2` shades with `color-mix()`:
|
|
307
|
+
|
|
308
|
+
```typescript
|
|
309
|
+
theme: {
|
|
310
|
+
extraThemes: [
|
|
311
|
+
{
|
|
312
|
+
id: 'forest',
|
|
313
|
+
label: 'Forest',
|
|
314
|
+
basedOn: 'light',
|
|
315
|
+
palette: {
|
|
316
|
+
'--color-primary': '#2f855a',
|
|
317
|
+
'--color-surface-raised': '#f7faf8',
|
|
318
|
+
},
|
|
319
|
+
},
|
|
320
|
+
],
|
|
321
|
+
}
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
If a custom primary/background pair has low contrast, set `--color-primary-contrast` explicitly.
|
|
325
|
+
It is used for text on filled primary buttons, tags, and selected button groups.
|
|
326
|
+
|
|
327
|
+
### Overriding any token
|
|
328
|
+
|
|
329
|
+
Every token can be overridden, not just the base colors. Overrides are applied in three levels,
|
|
330
|
+
from lowest to highest precedence:
|
|
331
|
+
|
|
332
|
+
1. `palettes.light` / `palettes.dark`, or an `extraThemes[].palette` — merged onto the built-in
|
|
333
|
+
palette for that theme.
|
|
334
|
+
2. `colors` — applied globally after the active palette, so it wins across every theme.
|
|
335
|
+
3. Runtime `document.documentElement.style.setProperty('--color-…', …)`.
|
|
336
|
+
|
|
337
|
+
Overriding a **seed** color (e.g. `--color-border`) also updates the derived tokens that
|
|
338
|
+
reference it (`--color-sidebar-border`, `--color-code-border`, `--color-table-border`,
|
|
339
|
+
`--color-blockquote-border`). Overriding a **derived** token affects only that token. Prefer
|
|
340
|
+
overriding seeds when the value should adapt across themes.
|
|
341
|
+
|
|
342
|
+
```typescript
|
|
343
|
+
theme: {
|
|
344
|
+
colors: {
|
|
345
|
+
'--color-theme-switch-hover': 'rgba(0, 0, 0, 0.08)',
|
|
346
|
+
},
|
|
347
|
+
palettes: {
|
|
348
|
+
light: {
|
|
349
|
+
'--color-sidebar-bg': '#f0f2f5',
|
|
350
|
+
},
|
|
351
|
+
},
|
|
352
|
+
}
|
|
353
|
+
```
|
|
354
|
+
|
|
268
355
|
## Shell actions (`shell.actions`)
|
|
269
356
|
|
|
270
357
|
Use `shell.actions` to render app-specific Vue components in the active layout's global toolbar.
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
the recommended place for a signed-in user avatar, notification
|
|
274
|
-
switcher.
|
|
358
|
+
On mobile, they appear at the right side of the mobile header. On desktop, they appear at the end
|
|
359
|
+
of the top header in tiered navigation, or in the sidebar footer after the built-in controls in
|
|
360
|
+
sidebar-only navigation. This is the recommended place for a signed-in user avatar, notification
|
|
361
|
+
button, help menu, or tenant switcher.
|
|
275
362
|
|
|
276
363
|
```typescript
|
|
277
364
|
export default defineConfig({
|
|
@@ -307,8 +394,8 @@ Set `align` to `'left'`, `'center'`, or `'right'` for sidebar-footer placement;
|
|
|
307
394
|
## Internationalization (`i18n`)
|
|
308
395
|
|
|
309
396
|
Set `i18n` to enable multi-language support. The framework adds a locale switcher to the header,
|
|
310
|
-
resolves every `LocalizedString` field (`title`, `nav[].label`, `footer`, `links[].title`)
|
|
311
|
-
the active locale, and exposes the current locale via `useLocale()` / `useLocalize()`.
|
|
397
|
+
resolves every `LocalizedString` field (`name`, `title`, `nav[].label`, `footer`, `links[].title`)
|
|
398
|
+
against the active locale, and exposes the current locale via `useLocale()` / `useLocalize()`.
|
|
312
399
|
|
|
313
400
|
```typescript
|
|
314
401
|
import { defineConfig } from '@bndynet/vue-site'
|
|
@@ -321,7 +408,8 @@ export default defineConfig({
|
|
|
321
408
|
],
|
|
322
409
|
defaultLocale: 'en',
|
|
323
410
|
},
|
|
324
|
-
|
|
411
|
+
name: { en: 'My Site', zh: '我的站点' }, // brand text next to the logo
|
|
412
|
+
title: { en: 'My Site', zh: '我的站点' }, // browser-tab title
|
|
325
413
|
footer: { en: '© 2026', zh: '© 2026 版权所有' },
|
|
326
414
|
nav: [
|
|
327
415
|
{ label: { en: 'Home', zh: '首页' }, icon: 'home', page: '../README.md' },
|
|
@@ -423,8 +511,8 @@ base file → first entry).
|
|
|
423
511
|
|
|
424
512
|
| Property | Default | Description |
|
|
425
513
|
|----------|---------|-------------|
|
|
426
|
-
| `locales` | discovered `locales/*.json` | `{ code, label, icon? }[]` — supported languages, in display order. Optional
|
|
427
|
-
| `defaultLocale` | `
|
|
514
|
+
| `locales` | discovered `locales/*.json` | `{ code, label, icon? }[]` — supported languages, in display order. Optional in CLI mode; without the CLI, codes are derived from `i18n.messages` keys, or from `defaultLocale` / `en` if omitted. First entry is the fallback |
|
|
515
|
+
| `defaultLocale` | first resolved locale, or `en` | Initial locale when nothing is stored and browser detection finds no match |
|
|
428
516
|
| `detectBrowser` | `true` | Detect the initial locale from `navigator.language(s)` on first visit |
|
|
429
517
|
| `storageKey` | `vue-site-locale` | localStorage key for the chosen locale |
|
|
430
518
|
| `messages` | auto-loaded from `locales/<code>.json` | `Record<LocaleCode, Record<string, string>>` — extra/override translations, merged over the auto-loaded files and built-in UI strings |
|
|
@@ -439,7 +527,7 @@ no `messages` field) and you don't even have to list the languages.
|
|
|
439
527
|
```jsonc
|
|
440
528
|
// locales/en.json — nested groups (recommended), flattened to dotted ids
|
|
441
529
|
{
|
|
442
|
-
"site": { "title": "My Site" },
|
|
530
|
+
"site": { "name": "My Site", "title": "My Site" },
|
|
443
531
|
"nav": { "home": "Home", "guide": "Guide" }
|
|
444
532
|
}
|
|
445
533
|
```
|
|
@@ -447,7 +535,7 @@ no `messages` field) and you don't even have to list the languages.
|
|
|
447
535
|
```jsonc
|
|
448
536
|
// locales/zh.json
|
|
449
537
|
{
|
|
450
|
-
"site": { "title": "我的站点" },
|
|
538
|
+
"site": { "name": "我的站点", "title": "我的站点" },
|
|
451
539
|
"nav": { "home": "首页", "guide": "指南" }
|
|
452
540
|
}
|
|
453
541
|
```
|
|
@@ -469,6 +557,7 @@ export default defineConfig({
|
|
|
469
557
|
],
|
|
470
558
|
defaultLocale: 'en',
|
|
471
559
|
},
|
|
560
|
+
name: tk('site.name'),
|
|
472
561
|
title: tk('site.title'),
|
|
473
562
|
nav: [
|
|
474
563
|
{ label: tk('nav.home'), icon: 'home', page: '../README.md' },
|
|
@@ -491,9 +580,12 @@ mixed freely — use `tk()` for shared/centrally managed text and an inline map
|
|
|
491
580
|
relative to the config directory. `code` is the file name (a `LocaleCode` like `en` or `zh-TW`).
|
|
492
581
|
- An explicit `i18n.messages` is still supported and **overrides** auto-loaded keys (per id); an
|
|
493
582
|
explicit `i18n.locales` controls the label/icon/order. Both are optional.
|
|
494
|
-
- Auto-discovery is a **CLI** feature. If you embed the library yourself
|
|
495
|
-
without the `vue-site` CLI), pass `i18n.messages` directly — e.g. build it
|
|
496
|
-
imports (avoid `import.meta.glob` inside `site.config.ts`, which the CLI
|
|
583
|
+
- Auto-discovery of `locales/<code>.json` is a **CLI** feature. If you embed the library yourself
|
|
584
|
+
(calling `createSiteApp` without the `vue-site` CLI), pass `i18n.messages` directly — e.g. build it
|
|
585
|
+
from JSON with explicit imports (avoid `import.meta.glob` inside `site.config.ts`, which the CLI
|
|
586
|
+
pre-loads in Node). When `i18n.locales` is omitted, the library derives locale codes from the
|
|
587
|
+
`messages` keys and uses those codes as display labels. For localized content without a messages
|
|
588
|
+
catalog, provide `locales` explicitly so the locale switcher has the intended options and labels.
|
|
497
589
|
|
|
498
590
|
### Localizing in your own pages
|
|
499
591
|
|
|
@@ -607,7 +699,7 @@ Notes:
|
|
|
607
699
|
| Property | Description |
|
|
608
700
|
|----------|-------------|
|
|
609
701
|
| `port` | Dev server port |
|
|
610
|
-
| `outDir` | Build output (relative to site root; default `
|
|
702
|
+
| `outDir` | Build output (relative to site root; default `dist`) |
|
|
611
703
|
| `customElements` | Tag prefixes for custom elements (e.g. `['chat-', 'i-']`) |
|
|
612
704
|
| `watchPackages` | Local packages — see [env.watchPackages](#envwatchpackages) |
|
|
613
705
|
| `vite` | Vite overrides (not `root`); framework merges aliases, `server.fs.allow`, `build.outDir`, etc. |
|
|
@@ -654,11 +746,11 @@ app.mount('#app')
|
|
|
654
746
|
|
|
655
747
|
Use a top-level `await` in your entry (or an async IIFE): `createSiteApp` is async and **awaits** `configureApp` when it returns a `Promise`. If you set optional `bootstrap` in config, that module loads before the app is created; if you omit `bootstrap`, that step is skipped.
|
|
656
748
|
|
|
657
|
-
Exports: `createSiteApp`, `defineConfig`, `useTheme`, `useSiteConfig`, `getSiteConfig`, `useLocale`, `useLocalize`, `tk`, `resolveLocalized`, `resolveField`, `resolveMessage`, `mergeCatalog`, `flattenMessages`, `isMessageRef`, `localizedPage`, `builtinMessages`, `themeRefKey`, `localeRefKey`. Types: `SiteConfig`, `SiteEnvConfig`, `SiteViteConfig`, `SiteCustomConfig`, `SiteExternalLink`, `ShellConfig`, `ShellAction`, `ShellActionLoader`, `NavItem`, `StandalonePage`, `PageLayout`, `AuthRule`, `AuthContext`, `AuthConfig`, `RouterConfig`, `ThemeConfig`, `ThemeOption`, `ThemePaletteVars`, `ResolvedNavItem`, `I18nConfig`, `LocaleOption`, `LocaleCode`, `LocalizedString`, `IconRegistry`, `MessageRef`, `MessageTree`, `MessageCatalog`, `PageLoader`, `LocalizedPageOptions`.
|
|
749
|
+
Exports: `createSiteApp`, `defineConfig`, `useTheme`, `useMobileViewport`, `useSiteConfig`, `getSiteConfig`, `useLocale`, `useLocalize`, `tk`, `resolveLocalized`, `resolveField`, `resolveMessage`, `mergeCatalog`, `flattenMessages`, `isMessageRef`, `localizedPage`, `builtinMessages`, `themeRefKey`, `localeRefKey`. Types: `SiteConfig`, `SiteEnvConfig`, `SiteViteConfig`, `SiteCustomConfig`, `SiteExternalLink`, `ShellConfig`, `ShellAction`, `ShellActionLoader`, `NavItem`, `StandalonePage`, `PageLayout`, `AuthRule`, `AuthContext`, `AuthConfig`, `RouterConfig`, `ThemeConfig`, `ThemeOption`, `ThemePaletteVars`, `ResolvedNavItem`, `I18nConfig`, `LocaleOption`, `LocaleCode`, `LocalizedString`, `IconRegistry`, `MessageRef`, `MessageTree`, `MessageCatalog`, `PageLoader`, `LocalizedPageOptions`.
|
|
658
750
|
|
|
659
751
|
### Theme in Vue pages (`useTheme`)
|
|
660
752
|
|
|
661
|
-
Import `useTheme` from `@bndynet/vue-site`. It returns a reactive `theme` ref (the active theme id, e.g. `light`, `dark`, or an `extraThemes[].id`) plus `setTheme` and `toggleTheme`. The root layout also sets `document.documentElement` attribute `data-theme
|
|
753
|
+
Import `useTheme` from `@bndynet/vue-site`. It returns a reactive `theme` ref (the active theme id, e.g. `light`, `dark`, or an `extraThemes[].id`) plus `setTheme` and `toggleTheme`. The root layout also sets `document.documentElement` attribute `data-theme`, applies CSS variables, and updates `color-scheme` so native browser controls and scrollbars use the active theme's light or dark scheme.
|
|
662
754
|
|
|
663
755
|
The theme ref is **provided** by `createSiteApp()` (same Vue runtime as your app), so `watch(theme, …)` works reliably even when another tool would otherwise load a second copy of `vue` from nested `node_modules`.
|
|
664
756
|
|
|
@@ -681,6 +773,28 @@ watch(theme, (next, prev) => {
|
|
|
681
773
|
|
|
682
774
|
`watch` only runs when the theme **changes** after your component is set up (for example after the user clicks the theme control). It does not run on the initial value unless you pass `{ immediate: true }`.
|
|
683
775
|
|
|
776
|
+
### Responsive viewport state (`useMobileViewport`)
|
|
777
|
+
|
|
778
|
+
Import `useMobileViewport` when component behavior needs to react to the same mobile breakpoint as
|
|
779
|
+
the site layout. It returns a read-only ref that is `true` at viewport widths of 840 CSS pixels or
|
|
780
|
+
narrower. The layout provides one shared ref, so components do not need to create their own media
|
|
781
|
+
query listeners. For visual-only changes, prefer CSS media queries.
|
|
782
|
+
|
|
783
|
+
```vue
|
|
784
|
+
<script setup lang="ts">
|
|
785
|
+
import { useMobileViewport } from '@bndynet/vue-site'
|
|
786
|
+
|
|
787
|
+
const isMobileViewport = useMobileViewport()
|
|
788
|
+
</script>
|
|
789
|
+
|
|
790
|
+
<template>
|
|
791
|
+
<button v-if="isMobileViewport">Open compact menu</button>
|
|
792
|
+
</template>
|
|
793
|
+
```
|
|
794
|
+
|
|
795
|
+
Call the composable from a Vue component rendered inside the site layout. It throws a `[vue-site]`
|
|
796
|
+
error when used outside that context.
|
|
797
|
+
|
|
684
798
|
## Upgrade
|
|
685
799
|
|
|
686
800
|
```bash
|
|
@@ -693,7 +807,7 @@ npm update @bndynet/vue-site
|
|
|
693
807
|
git clone https://github.com/bndynet/vue-site.git && cd vue-site
|
|
694
808
|
npm install
|
|
695
809
|
npm run dev # watch-build lib + example site
|
|
696
|
-
npm run build # `dist/` + `example/
|
|
810
|
+
npm run build # `dist/` + `example/dist`
|
|
697
811
|
```
|
|
698
812
|
|
|
699
813
|
### Using a local build in another project
|
package/README.zh.md
CHANGED
|
@@ -100,7 +100,7 @@ npx vue-site build --base /app/
|
|
|
100
100
|
| `title` | 站点标题(侧边栏 + 标签页)。`LocalizedString` |
|
|
101
101
|
| `nav` | `NavItem[]` |
|
|
102
102
|
| `navPosition` | 可选的导航位置:`'top'` 强制使用顶部/分层导航,`'sidebar'` 强制将完整导航树放在侧边栏。不传时保持基于导航深度的自动行为 |
|
|
103
|
-
| `defaultPath` | 站点打开时的路径;`/` 和未知路径会重定向到此。必须匹配一个已注册的路由(某个 `nav` 条目解析后的路径,或某个 `pages` 条目的 `path
|
|
103
|
+
| `defaultPath` | 站点打开时的路径;`/` 和未知路径会重定向到此。必须匹配一个已注册的路由(某个 `nav` 条目解析后的路径,或某个 `pages` 条目的 `path`)。默认选择导航顺序中的第一个已注册页面;如果导航中没有页面,则选择第一个可见的独立页面 |
|
|
104
104
|
| `favicon` | 浏览器标签页图标 URL。可使用 public 资源路径、远程 URL 或导入后的资源 URL |
|
|
105
105
|
| `logo` | Logo 的 URL 或导入的图片 |
|
|
106
106
|
| `theme` | 见下方 `ThemeConfig`;设为 `false` 可禁用主题(隐藏切换器、强制使用固定的 `light` 调色板、不进行 localStorage 持久化) |
|
|
@@ -222,6 +222,20 @@ export default defineConfig({
|
|
|
222
222
|
})
|
|
223
223
|
```
|
|
224
224
|
|
|
225
|
+
### 响应式辅助
|
|
226
|
+
|
|
227
|
+
宽内容——例如 Element Plus 的 `el-table` 或多列 `el-form`——在小屏幕上可能溢出。把它包进一个
|
|
228
|
+
`.site-responsive-scroll` 容器,即可获得横向滚动条(触屏惯性滚动 + 可见的滚动条提示),而不是
|
|
229
|
+
溢出页面:
|
|
230
|
+
|
|
231
|
+
```vue
|
|
232
|
+
<template>
|
|
233
|
+
<div class="site-responsive-scroll">
|
|
234
|
+
<el-table :data="rows">…</el-table>
|
|
235
|
+
</div>
|
|
236
|
+
</template>
|
|
237
|
+
```
|
|
238
|
+
|
|
225
239
|
### `ThemeConfig`
|
|
226
240
|
|
|
227
241
|
内置主题为 `light`、`dark`,外加始终可用的额外主题 `sepia` 和 `ocean`(在每个站点的切换器中都会显示)。在 `SiteConfig` 上设置 `theme: false` 可完全禁用主题。
|
|
@@ -233,6 +247,73 @@ export default defineConfig({
|
|
|
233
247
|
| `palettes` | — | 仅针对内置 light/dark 的部分覆盖 |
|
|
234
248
|
| `extraThemes` | — | 额外主题:`id`、`label`、`icon`,可选 `basedOn`、`palette`;复用内置 id(`sepia`/`ocean`)即可覆盖它。导入 `builtinThemePalettes` 获取完整默认值 |
|
|
235
249
|
|
|
250
|
+
### 语义化颜色变量
|
|
251
|
+
|
|
252
|
+
每个主题都由语义化的 `--color-*` CSS 变量驱动。Element Plus 的通用变量在
|
|
253
|
+
`element-plus-theme.css` 中映射到这些变量,因此常见控件和表面会跟随当前主题。部分组件
|
|
254
|
+
还有专属变量;新增 Element Plus 组件时,可能需要补充对应映射。内置调色板定义种子色,
|
|
255
|
+
其余值通过 `color-mix()` / `var()` 派生,因此 `builtinThemePalettes` 中可能包含 CSS 表达式,
|
|
256
|
+
而不只是字面色值。
|
|
257
|
+
|
|
258
|
+
| 变量 | 用途 |
|
|
259
|
+
|------|------|
|
|
260
|
+
| `--color-primary` | 品牌/主色(映射到 `--el-color-primary` 及其色阶) |
|
|
261
|
+
| `--color-primary-contrast` | 主色填充控件上的文字色,默认取当前主题背景色 |
|
|
262
|
+
| `--color-primary-hover` | 主色填充控件的悬停色,由主色和文字色派生 |
|
|
263
|
+
| `--color-success` | 成功 |
|
|
264
|
+
| `--color-warning` | 警告 |
|
|
265
|
+
| `--color-danger` | 危险/错误(`--el-color-error` 复用该值) |
|
|
266
|
+
| `--color-info` | 信息 |
|
|
267
|
+
| `--color-surface-raised` | 浮层表面:下拉、对话框、遮罩背景 |
|
|
268
|
+
| `--color-overlay` | 弹窗 / 加载遮罩 |
|
|
269
|
+
|
|
270
|
+
自定义主题只需定义这些基础色,框架会用 `color-mix()` 自动派生 Element Plus 的
|
|
271
|
+
`light-3/5/7/8/9` 与 `dark-2` 色阶:
|
|
272
|
+
|
|
273
|
+
```typescript
|
|
274
|
+
theme: {
|
|
275
|
+
extraThemes: [
|
|
276
|
+
{
|
|
277
|
+
id: 'forest',
|
|
278
|
+
label: 'Forest',
|
|
279
|
+
basedOn: 'light',
|
|
280
|
+
palette: {
|
|
281
|
+
'--color-primary': '#2f855a',
|
|
282
|
+
'--color-surface-raised': '#f7faf8',
|
|
283
|
+
},
|
|
284
|
+
},
|
|
285
|
+
],
|
|
286
|
+
}
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
如果自定义主色与背景的对比度不足,可以显式设置 `--color-primary-contrast`。它用于主色按钮、
|
|
290
|
+
深色标签和选中按钮组等填充控件上的文字。
|
|
291
|
+
|
|
292
|
+
### 覆盖任意 token
|
|
293
|
+
|
|
294
|
+
不只是基础色,**任何 token 都可以覆盖**。覆盖按三级优先级从低到高应用:
|
|
295
|
+
|
|
296
|
+
1. `palettes.light` / `palettes.dark`,或 `extraThemes[].palette` —— 合并到该主题的内置调色板。
|
|
297
|
+
2. `colors` —— 在激活调色板之后全局应用,对所有主题生效。
|
|
298
|
+
3. 运行时直接调用 `document.documentElement.style.setProperty('--color-…', …)`。
|
|
299
|
+
|
|
300
|
+
覆盖 **种子色**(如 `--color-border`)会同时更新引用它的派生 token(`--color-sidebar-border`、
|
|
301
|
+
`--color-code-border`、`--color-table-border`、`--color-blockquote-border`);覆盖 **派生 token**
|
|
302
|
+
则只影响它本身。如果希望颜色能随主题自动适配,优先覆盖种子色。
|
|
303
|
+
|
|
304
|
+
```typescript
|
|
305
|
+
theme: {
|
|
306
|
+
colors: {
|
|
307
|
+
'--color-theme-switch-hover': 'rgba(0, 0, 0, 0.08)',
|
|
308
|
+
},
|
|
309
|
+
palettes: {
|
|
310
|
+
light: {
|
|
311
|
+
'--color-sidebar-bg': '#f0f2f5',
|
|
312
|
+
},
|
|
313
|
+
},
|
|
314
|
+
}
|
|
315
|
+
```
|
|
316
|
+
|
|
236
317
|
## 国际化(`i18n`)
|
|
237
318
|
|
|
238
319
|
设置 `i18n` 以启用多语言支持。框架会在头部添加语言切换器,
|
|
@@ -340,8 +421,8 @@ export default defineConfig({
|
|
|
340
421
|
|
|
341
422
|
| 属性 | 默认值 | 说明 |
|
|
342
423
|
|----------|---------|-------------|
|
|
343
|
-
| `locales` | 自动发现的 `locales/*.json` | `{ code, label, icon? }[]` ——
|
|
344
|
-
| `defaultLocale` | `
|
|
424
|
+
| `locales` | 自动发现的 `locales/*.json` | `{ code, label, icon? }[]` —— 支持的语言,按显示顺序排列。CLI 模式下可省略;不使用 CLI 时,省略后从 `i18n.messages` 的键派生,若也没有则使用 `defaultLocale` 或 `en`。第一个条目为回退项 |
|
|
425
|
+
| `defaultLocale` | 第一个解析出的语言,或 `en` | 当没有存储值且检测未命中时使用的初始语言 |
|
|
345
426
|
| `detectBrowser` | `true` | 首次访问时从 `navigator.language(s)` 检测初始语言 |
|
|
346
427
|
| `storageKey` | `vue-site-locale` | 用于存储所选语言的 localStorage 键 |
|
|
347
428
|
| `messages` | 自动从 `locales/<code>.json` 加载 | `Record<LocaleCode, Record<string, string>>` —— 额外/覆盖的翻译,会合并到自动加载的文件和内置 UI 字符串之上 |
|
|
@@ -407,9 +488,11 @@ export default defineConfig({
|
|
|
407
488
|
`code` 即文件名(一个 `LocaleCode`,如 `en` 或 `zh-TW`)。
|
|
408
489
|
- 仍支持显式的 `i18n.messages`,并会**覆盖**自动加载的键(按 id);显式的 `i18n.locales`
|
|
409
490
|
控制 label/icon/顺序。两者均为可选。
|
|
410
|
-
-
|
|
411
|
-
|
|
412
|
-
`import.meta.glob`,CLI 会在 Node
|
|
491
|
+
- 自动发现 `locales/<code>.json` 是 **CLI** 特性。如果你自行嵌入该库(不使用 `vue-site` CLI 而直接调用
|
|
492
|
+
`createSiteApp`),请直接传入 `i18n.messages` —— 例如用显式 import 从 JSON 构建(避免在
|
|
493
|
+
`site.config.ts` 中使用 `import.meta.glob`,CLI 会在 Node 中预加载它)。省略 `i18n.locales` 时,库会
|
|
494
|
+
从 `messages` 的键派生语言代码,并直接用代码作为显示名称。若本地化内容没有消息目录,请显式提供
|
|
495
|
+
`locales`,以设置语言切换器的选项和名称。
|
|
413
496
|
|
|
414
497
|
### 在你自己的页面中本地化
|
|
415
498
|
|
|
@@ -590,14 +673,15 @@ app.mount('#app')
|
|
|
590
673
|
返回 `Promise` 时会 **await** 它。如果你在配置中设置了可选的 `bootstrap`,该模块会在应用创建前加载;
|
|
591
674
|
如果省略 `bootstrap`,则跳过该步骤。
|
|
592
675
|
|
|
593
|
-
导出:`createSiteApp`、`defineConfig`、`useTheme`、`useSiteConfig`、`getSiteConfig`、`useLocale`、`useLocalize`、`tk`、`resolveLocalized`、`resolveField`、`resolveMessage`、`mergeCatalog`、`flattenMessages`、`isMessageRef`、`localizedPage`、`builtinMessages`、`themeRefKey`、`localeRefKey`。类型:`SiteConfig`、`SiteEnvConfig`、`SiteViteConfig`、`SiteCustomConfig`、`SiteExternalLink`、`NavItem`、`StandalonePage`、`PageLayout`、`AuthRule`、`AuthContext`、`AuthConfig`、`RouterConfig`、`ThemeConfig`、`ThemeOption`、`ThemePaletteVars`、`ResolvedNavItem`、`I18nConfig`、`LocaleOption`、`LocaleCode`、`LocalizedString`、`MessageRef`、`MessageTree`、`MessageCatalog`、`PageLoader`、`LocalizedPageOptions`。
|
|
676
|
+
导出:`createSiteApp`、`defineConfig`、`useTheme`、`useMobileViewport`、`useSiteConfig`、`getSiteConfig`、`useLocale`、`useLocalize`、`tk`、`resolveLocalized`、`resolveField`、`resolveMessage`、`mergeCatalog`、`flattenMessages`、`isMessageRef`、`localizedPage`、`builtinMessages`、`themeRefKey`、`localeRefKey`。类型:`SiteConfig`、`SiteEnvConfig`、`SiteViteConfig`、`SiteCustomConfig`、`SiteExternalLink`、`NavItem`、`StandalonePage`、`PageLayout`、`AuthRule`、`AuthContext`、`AuthConfig`、`RouterConfig`、`ThemeConfig`、`ThemeOption`、`ThemePaletteVars`、`ResolvedNavItem`、`I18nConfig`、`LocaleOption`、`LocaleCode`、`LocalizedString`、`MessageRef`、`MessageTree`、`MessageCatalog`、`PageLoader`、`LocalizedPageOptions`。
|
|
594
677
|
|
|
595
678
|
### 在 Vue 页面中使用主题(`useTheme`)
|
|
596
679
|
|
|
597
680
|
从 `@bndynet/vue-site` 导入 `useTheme`。它返回一个响应式的 `theme` ref(当前激活的主题 id,
|
|
598
681
|
如 `light`、`dark` 或某个 `extraThemes[].id`),外加 `setTheme` 和 `toggleTheme`。
|
|
599
682
|
根布局还会在 `document.documentElement` 上设置 `data-theme` 属性并应用 CSS 变量,
|
|
600
|
-
因此你可以用 `var(--color-*)` 来设置样式,无需 JavaScript
|
|
683
|
+
因此你可以用 `var(--color-*)` 来设置样式,无需 JavaScript。它还会更新 `color-scheme`,
|
|
684
|
+
让浏览器原生控件和滚动条跟随当前主题的亮色或暗色模式。
|
|
601
685
|
|
|
602
686
|
theme ref 由 `createSiteApp()` **提供**(与你的应用同一个 Vue 运行时),所以 `watch(theme, …)`
|
|
603
687
|
能可靠工作,即使其他工具可能会从嵌套的 `node_modules` 加载第二份 `vue`。
|
|
@@ -622,6 +706,26 @@ watch(theme, (next, prev) => {
|
|
|
622
706
|
`watch` 仅在你的组件初始化之后主题**发生变化**时运行(例如用户点击主题控件之后)。
|
|
623
707
|
除非你传入 `{ immediate: true }`,否则它不会在初始值上运行。
|
|
624
708
|
|
|
709
|
+
### 响应式视口状态(`useMobileViewport`)
|
|
710
|
+
|
|
711
|
+
当组件行为需要和站点布局使用相同的移动端断点时,导入 `useMobileViewport`。它返回一个只读 ref;
|
|
712
|
+
视口宽度不超过 840 CSS 像素时值为 `true`。站点布局提供的是共享 ref,因此组件无需各自创建媒体查询监听器。
|
|
713
|
+
如果只是调整视觉样式,优先使用 CSS 媒体查询。
|
|
714
|
+
|
|
715
|
+
```vue
|
|
716
|
+
<script setup lang="ts">
|
|
717
|
+
import { useMobileViewport } from '@bndynet/vue-site'
|
|
718
|
+
|
|
719
|
+
const isMobileViewport = useMobileViewport()
|
|
720
|
+
</script>
|
|
721
|
+
|
|
722
|
+
<template>
|
|
723
|
+
<button v-if="isMobileViewport">打开紧凑菜单</button>
|
|
724
|
+
</template>
|
|
725
|
+
```
|
|
726
|
+
|
|
727
|
+
请在站点布局内部渲染的 Vue 组件中调用此 composable。在该上下文之外调用时,会抛出 `[vue-site]` 错误。
|
|
728
|
+
|
|
625
729
|
## 升级
|
|
626
730
|
|
|
627
731
|
```bash
|
package/bin/vue-site.mjs
CHANGED
|
@@ -129,6 +129,7 @@ const configCandidates = [
|
|
|
129
129
|
const initSiteConfig = `import { defineConfig } from '@bndynet/vue-site'
|
|
130
130
|
|
|
131
131
|
export default defineConfig({
|
|
132
|
+
name: 'My Site',
|
|
132
133
|
title: 'My Site',
|
|
133
134
|
// Public client configuration. Never place secrets here.
|
|
134
135
|
custom: {
|
|
@@ -227,6 +228,7 @@ function resolveSiteConfig(cliConfig) {
|
|
|
227
228
|
'Run `npx vue-site init` to create starter files, or create site.config.ts manually:\n\n' +
|
|
228
229
|
' import type { SiteConfig } from \'@bndynet/vue-site\'\n\n' +
|
|
229
230
|
' export default {\n' +
|
|
231
|
+
' name: \'My Site\',\n' +
|
|
230
232
|
' title: \'My Site\',\n' +
|
|
231
233
|
' nav: [\n' +
|
|
232
234
|
' { label: \'Home\', icon: \'home\', page: () => import(\'./README.md?raw\') },\n' +
|
|
@@ -443,7 +445,7 @@ function buildHtmlShell(scriptTag, siteConfig) {
|
|
|
443
445
|
<html lang="en">
|
|
444
446
|
<head>
|
|
445
447
|
<meta charset="UTF-8" />
|
|
446
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
448
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover" />
|
|
447
449
|
${buildFaviconLink(siteConfig)} <title></title>
|
|
448
450
|
</head>
|
|
449
451
|
<body>
|
|
@@ -1057,7 +1059,7 @@ async function buildViteConfig(options = {}) {
|
|
|
1057
1059
|
},
|
|
1058
1060
|
},
|
|
1059
1061
|
build: {
|
|
1060
|
-
outDir: resolve(cwd, outDir ||
|
|
1062
|
+
outDir: resolve(cwd, outDir || 'dist'),
|
|
1061
1063
|
emptyOutDir: true,
|
|
1062
1064
|
},
|
|
1063
1065
|
...(cliBase != null && { base: cliBase }),
|
|
@@ -1,2 +1,6 @@
|
|
|
1
|
-
declare const _default: import('vue').DefineComponent<{}, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {}, string, import('vue').PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, import('vue').ComponentProvideOptions, true, {
|
|
1
|
+
declare const _default: import('vue').DefineComponent<{}, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {}, string, import('vue').PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, import('vue').ComponentProvideOptions, true, {
|
|
2
|
+
menuButton: HTMLButtonElement;
|
|
3
|
+
mobileDrawer: HTMLElement;
|
|
4
|
+
drawerCloseButton: HTMLButtonElement;
|
|
5
|
+
}, HTMLDivElement>;
|
|
2
6
|
export default _default;
|
|
@@ -6,5 +6,6 @@ declare const _default: import('vue').DefineComponent<__VLS_Props, {}, {}, {}, {
|
|
|
6
6
|
}, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {
|
|
7
7
|
detailsRef: HTMLDetailsElement;
|
|
8
8
|
summaryRef: HTMLElement;
|
|
9
|
+
menuRef: HTMLDivElement;
|
|
9
10
|
}, HTMLDetailsElement>;
|
|
10
11
|
export default _default;
|
|
@@ -1,6 +1,14 @@
|
|
|
1
1
|
import { ResolvedNavItem } from '../types';
|
|
2
2
|
type __VLS_Props = {
|
|
3
3
|
item: ResolvedNavItem;
|
|
4
|
+
collapsed?: boolean;
|
|
5
|
+
hoverExpand?: boolean;
|
|
4
6
|
};
|
|
5
|
-
declare const _default: import('vue').DefineComponent<__VLS_Props, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {}, string, import('vue').PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {
|
|
7
|
+
declare const _default: import('vue').DefineComponent<__VLS_Props, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {}, string, import('vue').PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {
|
|
8
|
+
collapsed: boolean;
|
|
9
|
+
hoverExpand: boolean;
|
|
10
|
+
}, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {
|
|
11
|
+
trigger: HTMLButtonElement;
|
|
12
|
+
popup: HTMLDivElement;
|
|
13
|
+
}, HTMLDivElement>;
|
|
6
14
|
export default _default;
|
|
@@ -2,8 +2,10 @@ import { ResolvedNavItem } from '../types';
|
|
|
2
2
|
type __VLS_Props = {
|
|
3
3
|
item: ResolvedNavItem;
|
|
4
4
|
indent?: boolean;
|
|
5
|
+
collapsed?: boolean;
|
|
5
6
|
};
|
|
6
7
|
declare const _default: import('vue').DefineComponent<__VLS_Props, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {}, string, import('vue').PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {
|
|
7
8
|
indent: boolean;
|
|
9
|
+
collapsed: boolean;
|
|
8
10
|
}, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {}, any>;
|
|
9
11
|
export default _default;
|
|
@@ -1,2 +1,13 @@
|
|
|
1
|
-
|
|
1
|
+
import { ResolvedNavItem } from '../types';
|
|
2
|
+
type __VLS_Props = {
|
|
3
|
+
/** Optional navigation tree override, used by the mobile drawer. */
|
|
4
|
+
items?: ResolvedNavItem[];
|
|
5
|
+
/** Whether desktop navigation is shown as an icon rail. */
|
|
6
|
+
collapsed?: boolean;
|
|
7
|
+
};
|
|
8
|
+
declare const _default: import('vue').DefineComponent<__VLS_Props, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {
|
|
9
|
+
"toggle-collapse": () => any;
|
|
10
|
+
}, string, import('vue').PublicProps, Readonly<__VLS_Props> & Readonly<{
|
|
11
|
+
"onToggle-collapse"?: (() => any) | undefined;
|
|
12
|
+
}>, {}, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {}, HTMLElement>;
|
|
2
13
|
export default _default;
|
|
@@ -6,5 +6,6 @@ declare const _default: import('vue').DefineComponent<__VLS_Props, {}, {}, {}, {
|
|
|
6
6
|
}, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {
|
|
7
7
|
detailsRef: HTMLDetailsElement;
|
|
8
8
|
summaryRef: HTMLElement;
|
|
9
|
+
menuRef: HTMLDivElement;
|
|
9
10
|
}, HTMLDetailsElement>;
|
|
10
11
|
export default _default;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { Ref } from 'vue';
|
|
2
|
+
/**
|
|
3
|
+
* Breakpoint in CSS pixels below which the site switches to the mobile layout.
|
|
4
|
+
*
|
|
5
|
+
* CSS `@media (max-width: ...)` rules cannot read this value (media queries do not
|
|
6
|
+
* support `var()`), so any change here must also update the matching media queries in
|
|
7
|
+
* `src/styles/layout.css`, `src/styles/markdown.css`, `src/components/SideNav.vue`,
|
|
8
|
+
* and `src/components/ShellActions.vue`. Those files carry a `keep in sync` comment.
|
|
9
|
+
*/
|
|
10
|
+
export declare const MOBILE_BREAKPOINT_PX = 840;
|
|
11
|
+
/** Provide the active viewport state to components rendered by the site layout. */
|
|
12
|
+
export declare function provideMobileViewport(isMobileViewport: Ref<boolean>): void;
|
|
13
|
+
/**
|
|
14
|
+
* Return the site's reactive mobile viewport state.
|
|
15
|
+
*
|
|
16
|
+
* The value is `true` when the viewport is {@link MOBILE_BREAKPOINT_PX} CSS pixels wide or
|
|
17
|
+
* narrower. It is shared with the responsive site layout and updates when the viewport crosses
|
|
18
|
+
* that breakpoint.
|
|
19
|
+
*
|
|
20
|
+
* This composable must be called from a Vue component rendered inside `createSiteApp()`.
|
|
21
|
+
* The returned ref is read-only; use CSS media queries for presentation-only changes.
|
|
22
|
+
*
|
|
23
|
+
* @example
|
|
24
|
+
* ```vue
|
|
25
|
+
* <script setup lang="ts">
|
|
26
|
+
* import { useMobileViewport } from '@bndynet/vue-site'
|
|
27
|
+
*
|
|
28
|
+
* const isMobileViewport = useMobileViewport()
|
|
29
|
+
* </script>
|
|
30
|
+
*
|
|
31
|
+
* <template>
|
|
32
|
+
* <button v-if="isMobileViewport">Open compact menu</button>
|
|
33
|
+
* </template>
|
|
34
|
+
* ```
|
|
35
|
+
*/
|
|
36
|
+
export declare function useMobileViewport(): Readonly<Ref<boolean>>;
|
package/dist/index.d.ts
CHANGED
|
@@ -3,6 +3,7 @@ export { createSiteApp } from './create-app';
|
|
|
3
3
|
export { useTheme, themeRefKey } from './composables/useTheme';
|
|
4
4
|
export { useLocale, localeRefKey } from './composables/useLocale';
|
|
5
5
|
export { useLocalize } from './composables/useLocalize';
|
|
6
|
+
export { useMobileViewport } from './composables/useMobileViewport';
|
|
6
7
|
export { resolveLocalized, resolveField, resolveMessage, mergeCatalog, flattenMessages, tk, isMessageRef, localizedPage } from './i18n-utils';
|
|
7
8
|
export type { LocalizedPageOptions, MessageCatalog } from './i18n-utils';
|
|
8
9
|
export { builtinMessages } from './i18n-messages';
|