@meddleware/vitepress-theme 0.0.0-rename.0 → 0.0.1

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/CHANGELOG.md ADDED
@@ -0,0 +1,14 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [0.0.1] - 2026-10-31
9
+
10
+ ### Added
11
+
12
+ - First release. The VitePress default theme mapped onto `@meddleware/design-tokens` (colour mode, seasons, WCAG AAA contrast), a layout
13
+ with the Meddleware footer, the `baseConfig` site config, and landmark fixes for the default theme, extracted from the byte-identical
14
+ copies in docs-site and dev-site.
package/LICENSE ADDED
@@ -0,0 +1,14 @@
1
+ BSD Zero Clause License
2
+
3
+ Copyright (c) 2026 MeddleWare
4
+
5
+ Permission to use, copy, modify, and/or distribute this software for any
6
+ purpose with or without fee is hereby granted.
7
+
8
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
9
+ REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
10
+ AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
11
+ INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
12
+ LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
13
+ OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
14
+ PERFORMANCE OF THIS SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,56 @@
1
+ # @meddleware/vitepress-theme
2
+
3
+ The Meddleware [VitePress](https://vitepress.dev) theme and site config. It is the default theme with every colour, surface and font
4
+ taken from [`@meddleware/design-tokens`](https://github.com/meddleware-org/design-tokens), so a site follows the colour mode and the
5
+ seasons and keeps the tokens' WCAG AAA contrast (7:1 for text and labels), plus a footer and the config the sites share.
6
+
7
+ ## Use
8
+
9
+ ```ts
10
+ // docs/.vitepress/theme/index.ts
11
+ export { default } from '@meddleware/vitepress-theme'
12
+ ```
13
+
14
+ ```ts
15
+ // docs/.vitepress/config.ts
16
+ import { baseConfig } from '@meddleware/vitepress-theme/config'
17
+
18
+ export default baseConfig({
19
+ configUrl: import.meta.url, // docs/ is the source directory, ../dist the output
20
+ title: 'Meddleware Docs',
21
+ description: '…',
22
+ nav: [{ text: 'Guide', link: '/guide' }],
23
+ sidebar: { '/': [{ text: 'Guide', items: [{ text: 'Overview', link: '/' }] }] },
24
+ // appearance: 'dark' (default) | true | false, head: [...], extra: { ...anything else VitePress takes }
25
+ })
26
+ ```
27
+
28
+ A site that needs more extends the theme: `{ extends: MeddlewareTheme, enhanceApp() {…} }`.
29
+
30
+ `baseConfig` sets the language (`en-GB`), clean URLs, the favicon, the browser-chrome colour (from the tokens), local search, the GitHub
31
+ link, the high-contrast syntax themes (with comments as readable as the rest) and `ssr.noExternal` for `@meddleware/*`, which ship Vue
32
+ components that import their own CSS. `themeConfig`, `vite` and `markdown` in `extra` are merged one level down.
33
+
34
+ ## What the theme does
35
+
36
+ | | |
37
+ | --- | --- |
38
+ | `theme.css` | maps every `--vp-*` colour onto a token role; tone blocks and badges keep body-colour text with the tone as the border; nothing is purple |
39
+ | `MeddlewareLayout.vue` | the default layout plus `AppFooter` and `CopyrightLine` (a `contentinfo` landmark) |
40
+ | `landmarks.js` | a Vite plugin that gives home, `page` and 404 pages a `<main>`, and turns the mid-page doc footer into a `div` (it was a second `contentinfo`); exact edits that fail the build if VitePress changes |
41
+ | `index.ts` | imports the tokens, the seasons and `@meddleware/ui/reset.css`, and sets the season from the calendar |
42
+
43
+ The reset is declared in a cascade layer, and VitePress's own CSS is unlayered, so it wins wherever it styles an element.
44
+
45
+ ## Peers
46
+
47
+ `vitepress` (exact: 2.x alpha), `vue`, `@meddleware/ui`, `@meddleware/design-tokens`.
48
+
49
+ ## Development
50
+
51
+ `npm run build:site` builds the fixture site in `docs/`; `npm run test:e2e` runs axe (WCAG 2.2 AA and AAA) over it in every colour mode ×
52
+ season (`E2E_BROWSERS=all` for five browsers); `npm run shots` captures the screenshot matrix.
53
+
54
+ ## Licence
55
+
56
+ 0BSD.
@@ -0,0 +1,28 @@
1
+ /**
2
+ * @typedef {import('vitepress').UserConfig<import('vitepress').DefaultTheme.Config>} SiteConfig
3
+ */
4
+ /**
5
+ * @param {object} options
6
+ * @param {string} options.configUrl `import.meta.url` of the site's `docs/.vitepress/config.ts`: the source directory is `docs/` and the
7
+ * output the repository's `dist/` (a Dockerfile copies `dist/` into the static server).
8
+ * @param {string} options.title
9
+ * @param {string} options.description
10
+ * @param {import('vitepress').DefaultTheme.NavItem[]} [options.nav]
11
+ * @param {import('vitepress').DefaultTheme.Sidebar} [options.sidebar]
12
+ * @param {'dark' | true | false | 'force-dark' | 'force-auto'} [options.appearance] The colour mode a first visit gets (the toggle always
13
+ * works). Default `dark`, the estate's default.
14
+ * @param {SiteConfig['head']} [options.head] Extra `<head>` entries (the favicon and the browser-chrome colour are always there).
15
+ * @param {SiteConfig} [options.extra] Anything else, merged over the defaults (`themeConfig` and `vite` are merged one level down).
16
+ * @returns {SiteConfig}
17
+ */
18
+ export function baseConfig({ configUrl, title, description, nav, sidebar, appearance, head, extra }: {
19
+ configUrl: string;
20
+ title: string;
21
+ description: string;
22
+ nav?: import("vitepress").DefaultTheme.NavItem[] | undefined;
23
+ sidebar?: import("vitepress").DefaultTheme.Sidebar | undefined;
24
+ appearance?: boolean | "dark" | "force-dark" | "force-auto" | undefined;
25
+ head?: SiteConfig["head"];
26
+ extra?: import("vitepress").UserConfig<import("vitepress").DefaultTheme.Config> | undefined;
27
+ }): SiteConfig;
28
+ export type SiteConfig = import("vitepress").UserConfig<import("vitepress").DefaultTheme.Config>;
@@ -0,0 +1,7 @@
1
+ /**
2
+ * A Vite plugin that applies {@link EDITS} to the default theme's components.
3
+ * @returns {import('vite').Plugin}
4
+ */
5
+ export function landmarks(): import("vite").Plugin;
6
+ /** @type {Record<string, (code: string) => string>} */
7
+ export const EDITS: Record<string, (code: string) => string>;
package/package.json CHANGED
@@ -1 +1,94 @@
1
- {"name":"@meddleware/vitepress-theme","version":"0.0.0-rename.0","description":"Placeholder: the real releases are published from CI (see https://github.com/meddleware-org/vitepress-theme).","license":"0BSD","repository":{"type":"git","url":"git+https://github.com/meddleware-org/vitepress-theme.git"},"publishConfig":{"access":"public"}}
1
+ {
2
+ "name": "@meddleware/vitepress-theme",
3
+ "version": "0.0.1",
4
+ "type": "module",
5
+ "description": "The Meddleware VitePress theme and config: the default theme mapped onto the design tokens (colour mode, seasons, WCAG AAA contrast), a footer, and a shared site config.",
6
+ "license": "0BSD",
7
+ "author": "MeddleWare <dev@meddleware.co.uk>",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "git+https://github.com/meddleware-org/vitepress-theme.git"
11
+ },
12
+ "keywords": [
13
+ "vitepress",
14
+ "theme",
15
+ "design-tokens",
16
+ "accessibility"
17
+ ],
18
+ "files": [
19
+ "src",
20
+ "dist",
21
+ "CHANGELOG.md"
22
+ ],
23
+ "exports": {
24
+ ".": {
25
+ "types": "./src/index.ts",
26
+ "default": "./src/index.ts"
27
+ },
28
+ "./config": {
29
+ "types": "./dist/config.d.ts",
30
+ "default": "./src/config.js"
31
+ },
32
+ "./theme.css": "./src/theme.css"
33
+ },
34
+ "sideEffects": [
35
+ "*.css",
36
+ "*.vue"
37
+ ],
38
+ "engines": {
39
+ "node": ">=24"
40
+ },
41
+ "scripts": {
42
+ "build": "rm -rf dist && tsc -p tsconfig.build.json",
43
+ "prepublishOnly": "npm run build",
44
+ "type-check": "vue-tsc --noEmit",
45
+ "test": "vitest run",
46
+ "build:site": "vitepress build docs",
47
+ "lint:css": "stylelint \"src/**/*.{css,vue}\"",
48
+ "lint:js": "eslint .",
49
+ "lint:html": "html-validate \"src/**/*.vue\"",
50
+ "lint": "npm run lint:css && npm run lint:js && npm run lint:html",
51
+ "test:e2e": "npm run build:site && playwright test",
52
+ "shots": "SHOTS=1 playwright test"
53
+ },
54
+ "peerDependencies": {
55
+ "@meddleware/design-tokens": "^0.1.12",
56
+ "@meddleware/ui": "^0.1.36",
57
+ "vitepress": "2.0.0-alpha.20",
58
+ "vue": "^3.5.0"
59
+ },
60
+ "devDependencies": {
61
+ "@meddleware/design-tokens": "^0.1.12",
62
+ "@meddleware/frontend-config": "^0.0.1",
63
+ "@meddleware/ui": "^0.1.36",
64
+ "@playwright/test": "~1.64.1",
65
+ "@types/node": "~26.6.5",
66
+ "@vue/eslint-config-typescript": "^14.9.0",
67
+ "autoprefixer": "^10.6.2",
68
+ "eslint": "^10.13.0",
69
+ "eslint-plugin-vue": "^10.12.0",
70
+ "eslint-plugin-vuejs-accessibility": "^2.6.3",
71
+ "html-validate": "^11.17.0",
72
+ "html-validate-vue": "^9.3.1",
73
+ "jiti": "^2.7.0",
74
+ "postcss": "^8.5.32",
75
+ "postcss-html": "^2.0.0",
76
+ "stylelint": "^17.16.0",
77
+ "stylelint-config-standard": "^40.0.0",
78
+ "typescript": "~6.0.0",
79
+ "vitepress": "2.0.0-alpha.20",
80
+ "vitest": "~5.0.5",
81
+ "vue": "^3.5.44",
82
+ "vue-eslint-parser": "^10.4.1",
83
+ "vue-tsc": "~3.3.13"
84
+ },
85
+ "publishConfig": {
86
+ "access": "public"
87
+ },
88
+ "browserslist": [
89
+ "extends @meddleware/frontend-config/browserslist-config"
90
+ ],
91
+ "dependencies": {
92
+ "shiki": "^4.4.3"
93
+ }
94
+ }
@@ -0,0 +1,17 @@
1
+ <script setup lang="ts">
2
+ // The default layout with the Meddleware footer: the copyright line and the contact of the organisation, in a `contentinfo` landmark.
3
+ import DefaultTheme from 'vitepress/theme'
4
+ import { AppFooter, CopyrightLine } from '@meddleware/ui'
5
+
6
+ const { Layout } = DefaultTheme
7
+ </script>
8
+
9
+ <template>
10
+ <Layout>
11
+ <template #layout-bottom>
12
+ <AppFooter>
13
+ <CopyrightLine symbol-variant="kopimi" organisation-name="Meddleware" rights-statement="jam" />
14
+ </AppFooter>
15
+ </template>
16
+ </Layout>
17
+ </template>
package/src/config.js ADDED
@@ -0,0 +1,105 @@
1
+ // @ts-check
2
+ // The site config every Meddleware VitePress site shares: its language, URLs, appearance, colours in the browser chrome, local search,
3
+ // social link, the high-contrast syntax themes, and the build settings that let VitePress render the design-system components.
4
+ //
5
+ // // docs/.vitepress/config.ts
6
+ // import { baseConfig } from '@meddleware/vitepress-theme/config'
7
+ // export default baseConfig({ configUrl: import.meta.url, title: 'Meddleware Docs', description: '…', nav, sidebar })
8
+ import { fileURLToPath } from 'node:url'
9
+ import { defineConfig } from 'vitepress'
10
+ import tokens from '@meddleware/design-tokens/tokens.json' with { type: 'json' }
11
+ import githubLight from 'shiki/themes/github-light-high-contrast.mjs'
12
+ import githubDark from 'shiki/themes/github-dark-high-contrast.mjs'
13
+ import { landmarks } from './landmarks.js'
14
+
15
+ /**
16
+ * A syntax theme with its comments in `colour`. The high-contrast GitHub themes keep every token at 7:1 except comments (5:1, which
17
+ * the theme treats as exempt); here a comment is text like any other.
18
+ * @param {import('shiki').ThemeRegistration} theme
19
+ * @param {string} colour
20
+ * @returns {import('shiki').ThemeRegistration}
21
+ */
22
+ function readableComments(theme, colour) {
23
+ return {
24
+ ...theme,
25
+ name: `${theme.name}-readable-comments`,
26
+ tokenColors: (theme.tokenColors ?? []).map((t) =>
27
+ [t.scope ?? []].flat().includes('comment') ? { ...t, settings: { ...t.settings, foreground: colour } } : t,
28
+ ),
29
+ }
30
+ }
31
+
32
+ /**
33
+ * @typedef {import('vitepress').UserConfig<import('vitepress').DefaultTheme.Config>} SiteConfig
34
+ */
35
+
36
+ /**
37
+ * @param {object} options
38
+ * @param {string} options.configUrl `import.meta.url` of the site's `docs/.vitepress/config.ts`: the source directory is `docs/` and the
39
+ * output the repository's `dist/` (a Dockerfile copies `dist/` into the static server).
40
+ * @param {string} options.title
41
+ * @param {string} options.description
42
+ * @param {import('vitepress').DefaultTheme.NavItem[]} [options.nav]
43
+ * @param {import('vitepress').DefaultTheme.Sidebar} [options.sidebar]
44
+ * @param {'dark' | true | false | 'force-dark' | 'force-auto'} [options.appearance] The colour mode a first visit gets (the toggle always
45
+ * works). Default `dark`, the estate's default.
46
+ * @param {SiteConfig['head']} [options.head] Extra `<head>` entries (the favicon and the browser-chrome colour are always there).
47
+ * @param {SiteConfig} [options.extra] Anything else, merged over the defaults (`themeConfig` and `vite` are merged one level down).
48
+ * @returns {SiteConfig}
49
+ */
50
+ export function baseConfig({ configUrl, title, description, nav = [], sidebar = {}, appearance = 'dark', head = [], extra = {} }) {
51
+ const { themeConfig: extraTheme, vite: extraVite, markdown: extraMarkdown, ...rest } = extra
52
+ const { light, dark } = tokens.semantic
53
+ // The browser chrome follows the page: one colour when the site starts dark, a light and a dark one otherwise. Both are the page
54
+ // background of the tokens, so they cannot drift from the theme.
55
+ /** @type {import('vitepress').HeadConfig[]} */
56
+ const chrome =
57
+ appearance === 'dark' || appearance === 'force-dark'
58
+ ? [['meta', { name: 'theme-color', content: dark.bg }]]
59
+ : [
60
+ ['meta', { name: 'theme-color', content: light.bg, media: '(prefers-color-scheme: light)' }],
61
+ ['meta', { name: 'theme-color', content: dark.bg, media: '(prefers-color-scheme: dark)' }],
62
+ ]
63
+
64
+ return defineConfig({
65
+ title,
66
+ description,
67
+ lang: 'en-GB',
68
+ srcDir: fileURLToPath(new URL('..', configUrl)),
69
+ outDir: fileURLToPath(new URL('../../dist', configUrl)),
70
+ // The project's security audit lives in docs/audit (the audit corpus convention); it is not site content.
71
+ srcExclude: ['audit/**'],
72
+ cleanUrls: true,
73
+ lastUpdated: false,
74
+ appearance,
75
+ head: [
76
+ ['link', { rel: 'icon', type: 'image/svg+xml', href: '/favicon.svg' }],
77
+ ...chrome,
78
+ ...head,
79
+ ],
80
+ markdown: {
81
+ // The high-contrast syntax themes with readable comments, so code keeps AAA contrast on the token surfaces in both colour modes.
82
+ theme: { light: readableComments(githubLight, light.muted), dark: readableComments(githubDark, dark.muted) },
83
+ ...extraMarkdown,
84
+ },
85
+ vite: {
86
+ ...extraVite,
87
+ // Landmark fixes for the default theme (see landmarks.js).
88
+ plugins: [landmarks(), ...(extraVite?.plugins ?? [])],
89
+ ssr: {
90
+ ...extraVite?.ssr,
91
+ // The design-system packages ship Vue components and import their own CSS, which Node cannot load: Vite must process them
92
+ // while rendering the static pages.
93
+ noExternal: [/^@meddleware\//, ...(Array.isArray(extraVite?.ssr?.noExternal) ? extraVite.ssr.noExternal : [])],
94
+ },
95
+ },
96
+ themeConfig: {
97
+ search: { provider: 'local' },
98
+ socialLinks: [{ icon: 'github', link: 'https://github.com/meddleware-org' }],
99
+ nav,
100
+ sidebar,
101
+ ...extraTheme,
102
+ },
103
+ ...rest,
104
+ })
105
+ }
package/src/env.d.ts ADDED
@@ -0,0 +1,6 @@
1
+ /// <reference types="vite/client" />
2
+ declare module '*.vue' {
3
+ import type { DefineComponent } from 'vue'
4
+ const component: DefineComponent<object, object, unknown>
5
+ export default component
6
+ }
package/src/index.ts ADDED
@@ -0,0 +1,25 @@
1
+ // The Meddleware VitePress theme: the default theme + the design tokens (colour mode, seasons, AAA contrast) + a footer.
2
+ //
3
+ // // docs/.vitepress/theme/index.ts
4
+ // export { default } from '@meddleware/vitepress-theme'
5
+ //
6
+ // A site that needs more extends it: `{ extends: MeddlewareTheme, enhanceApp() {…} }`.
7
+ import DefaultTheme from 'vitepress/theme'
8
+ import type { Theme } from 'vitepress'
9
+ import { useSeason } from '@meddleware/ui'
10
+ import MeddlewareLayout from './MeddlewareLayout.vue'
11
+ import '@meddleware/design-tokens/tokens.css'
12
+ import '@meddleware/design-tokens/seasons.css'
13
+ import '@meddleware/ui/reset.css'
14
+ import './theme.css'
15
+
16
+ const theme: Theme = {
17
+ extends: DefaultTheme,
18
+ Layout: MeddlewareLayout,
19
+ enhanceApp() {
20
+ // Browser only (a no-op while rendering the static pages): the season follows the calendar.
21
+ useSeason()
22
+ },
23
+ }
24
+
25
+ export default theme
@@ -0,0 +1,57 @@
1
+ // @ts-check
2
+ // Landmark fixes for the VitePress default theme, applied to its source while Vite compiles it.
3
+ //
4
+ // Two of the default theme's pages fail WCAG's landmark rules, and the theme gives no slot to fix them from outside:
5
+ // - The home, `page` layout and 404 pages have no `<main>`: their content sits in a `div#VPContent`. The doc layout has its own
6
+ // `<main>`, so only the other layouts get one (two nested mains would be invalid).
7
+ // - The doc footer (edit link, last updated, previous/next) is a `<footer>` in the middle of the page, so it is a second `contentinfo`
8
+ // landmark next to the site footer. It is a plain `div`; the previous/next links are already a named `nav`.
9
+ //
10
+ // Each edit is an exact string replacement that throws when the text is not found: a VitePress upgrade that changes these files
11
+ // fails the build, with the name of the file, instead of silently losing the fix. `tests/landmarks.test.ts` runs the edits against the
12
+ // installed VitePress.
13
+
14
+ /**
15
+ * @param {string} code
16
+ * @param {string} from
17
+ * @param {string} to
18
+ * @param {string} file For the error message.
19
+ */
20
+ function replaceOnce(code, from, to, file) {
21
+ const first = code.indexOf(from)
22
+ if (first === -1 || code.indexOf(from, first + 1) !== -1) {
23
+ throw new Error(`@meddleware/vitepress-theme: cannot patch ${file}: expected exactly one occurrence of ${JSON.stringify(from)} (VitePress changed? update src/landmarks.js).`)
24
+ }
25
+ return code.replace(from, to)
26
+ }
27
+
28
+ /** @type {Record<string, (code: string) => string>} */
29
+ export const EDITS = {
30
+ 'VPContent.vue': (code) => {
31
+ let out = replaceOnce(code, 'const { isHome, hasSidebar } = useLayout();', "const { isHome, hasSidebar } = useLayout();\nconst landmark = page.value.isNotFound || ['page', 'home'].includes(frontmatter.value.layout) ? 'main' : 'div';", 'VPContent.vue')
32
+ out = replaceOnce(out, '<div\n class="VPContent"', '<component\n :is="landmark"\n class="VPContent"', 'VPContent.vue')
33
+ return replaceOnce(out, '<component v-else :is="frontmatter.layout || \'doc\'" />\n </div>\n</template>', '<component v-else :is="frontmatter.layout || \'doc\'" />\n </component>\n</template>', 'VPContent.vue')
34
+ },
35
+ 'VPDocFooter.vue': (code) => {
36
+ const out = replaceOnce(code, '<footer v-if="showFooter" class="VPDocFooter">', '<div v-if="showFooter" class="VPDocFooter">', 'VPDocFooter.vue')
37
+ return replaceOnce(out, '</footer>\n</template>', '</div>\n</template>', 'VPDocFooter.vue')
38
+ },
39
+ }
40
+
41
+ /**
42
+ * A Vite plugin that applies {@link EDITS} to the default theme's components.
43
+ * @returns {import('vite').Plugin}
44
+ */
45
+ export function landmarks() {
46
+ return {
47
+ name: 'meddleware:vitepress-landmarks',
48
+ enforce: 'pre',
49
+ transform(code, id) {
50
+ if (id.includes('?')) return null
51
+ const normalised = id.replaceAll('\\', '/')
52
+ if (!normalised.includes('/theme-default/components/')) return null
53
+ const edit = EDITS[normalised.slice(normalised.lastIndexOf('/') + 1)]
54
+ return edit ? { code: edit(code), map: null } : null
55
+ },
56
+ }
57
+ }
package/src/theme.css ADDED
@@ -0,0 +1,152 @@
1
+ /*
2
+ * The VitePress default theme, mapped onto the Meddleware design tokens.
3
+ *
4
+ * Every colour, surface and font of the default theme is a role from @meddleware/design-tokens, so the site follows the colour mode
5
+ * (VitePress's `.dark` class on <html>, which the tokens honour) and the seasons, and keeps the tokens' WCAG AAA contrast (7:1 for
6
+ * text and labels) by construction: nothing here is a colour of its own, only a role or a mix of roles.
7
+ *
8
+ * Declared on `:root, .dark` and loaded after VitePress's own variables, so it wins over them at equal specificity.
9
+ *
10
+ * Tone rules, as in @meddleware/ui: text in a tone colour over a tint of that tone loses contrast, so a tone block or badge keeps
11
+ * body-colour text and shows the tone as its border. Tinted backgrounds ("-soft") are the --lift surface, on which every text role
12
+ * is verified at 7:1 by the token checks; only diff and warning lines in code blocks are tinted (they hold syntax-theme colours).
13
+ * Nothing in the palette is purple (VitePress's "important" tone is the accent here).
14
+ */
15
+
16
+ :root,
17
+ .dark {
18
+ --vp-font-family-base: var(--mw-font-sans);
19
+ --vp-font-family-mono: var(--mw-font-mono);
20
+
21
+ /* Surfaces and text. */
22
+ --vp-c-bg: var(--bg);
23
+ --vp-c-bg-alt: var(--lift);
24
+ --vp-c-bg-elv: var(--surface);
25
+ --vp-c-bg-soft: var(--surface);
26
+ --vp-c-border: var(--border);
27
+ --vp-c-divider: var(--border);
28
+ --vp-c-gutter: var(--lift);
29
+ --vp-c-text-1: var(--text);
30
+ --vp-c-text-2: var(--muted);
31
+ --vp-c-text-3: var(--muted);
32
+
33
+ /* Neutral ("default") family: buttons, code backgrounds. */
34
+ --vp-c-gray-1: var(--border);
35
+ --vp-c-gray-2: var(--lift);
36
+ --vp-c-gray-3: var(--surface);
37
+ --vp-c-gray-soft: var(--lift);
38
+
39
+ /* Brand: the accent. -1 is text on the page, -3 a fill with --accent-contrast text, -2 the hover state (toward the text colour, so
40
+ contrast only grows), -soft a tint. */
41
+ --vp-c-brand-1: var(--accent);
42
+ --vp-c-brand-2: color-mix(in srgb, var(--accent) 82%, var(--text));
43
+ --vp-c-brand-3: var(--accent);
44
+ --vp-c-brand-soft: var(--lift);
45
+
46
+ /* Tones. */
47
+ --vp-c-tip-1: var(--secondary);
48
+ --vp-c-tip-2: color-mix(in srgb, var(--secondary) 82%, var(--text));
49
+ --vp-c-tip-3: var(--secondary);
50
+ --vp-c-tip-soft: var(--lift);
51
+ --vp-c-note-1: var(--secondary);
52
+ --vp-c-note-2: color-mix(in srgb, var(--secondary) 82%, var(--text));
53
+ --vp-c-note-3: var(--secondary);
54
+ --vp-c-note-soft: var(--lift);
55
+ --vp-c-success-1: var(--ok);
56
+ --vp-c-success-2: color-mix(in srgb, var(--ok) 82%, var(--text));
57
+ --vp-c-success-3: var(--ok);
58
+ --vp-c-success-soft: var(--lift);
59
+ --vp-c-important-1: var(--accent);
60
+ --vp-c-important-2: color-mix(in srgb, var(--accent) 82%, var(--text));
61
+ --vp-c-important-3: var(--accent);
62
+ --vp-c-important-soft: var(--lift);
63
+ --vp-c-warning-1: var(--warning-text);
64
+ --vp-c-warning-2: color-mix(in srgb, var(--warning-text) 82%, var(--text));
65
+ --vp-c-warning-3: var(--warning-text);
66
+ --vp-c-warning-soft: var(--lift);
67
+ --vp-c-danger-1: var(--danger);
68
+ --vp-c-danger-2: color-mix(in srgb, var(--danger) 82%, var(--text));
69
+ --vp-c-danger-3: var(--danger);
70
+ --vp-c-danger-soft: var(--lift);
71
+ --vp-c-caution-1: var(--danger);
72
+ --vp-c-caution-2: color-mix(in srgb, var(--danger) 82%, var(--text));
73
+ --vp-c-caution-3: var(--danger);
74
+ --vp-c-caution-soft: var(--lift);
75
+
76
+ /* Buttons: a brand fill carries the label colour made for it, not white. */
77
+ --vp-button-brand-text: var(--accent-contrast);
78
+ --vp-button-brand-hover-text: var(--accent-contrast);
79
+ --vp-button-brand-active-text: var(--accent-contrast);
80
+ --vp-button-brand-bg: var(--accent);
81
+ --vp-button-brand-hover-bg: color-mix(in srgb, var(--accent) 82%, var(--text));
82
+ --vp-button-brand-active-bg: color-mix(in srgb, var(--accent) 66%, var(--text));
83
+ --vp-button-alt-text: var(--text);
84
+ --vp-button-alt-hover-text: var(--text);
85
+ --vp-button-alt-active-text: var(--text);
86
+
87
+ /* Custom blocks and badges: body-colour text; the tone is the border and the tint. */
88
+ --vp-custom-block-info-border: var(--border);
89
+ --vp-custom-block-tip-border: var(--secondary);
90
+ --vp-custom-block-tip-text: var(--text);
91
+ --vp-custom-block-note-border: var(--secondary);
92
+ --vp-custom-block-note-text: var(--text);
93
+ --vp-custom-block-important-border: var(--accent);
94
+ --vp-custom-block-important-text: var(--text);
95
+ --vp-custom-block-warning-border: var(--warning-text);
96
+ --vp-custom-block-warning-text: var(--text);
97
+ --vp-custom-block-danger-border: var(--danger);
98
+ --vp-custom-block-danger-text: var(--text);
99
+ --vp-custom-block-caution-border: var(--danger);
100
+ --vp-custom-block-caution-text: var(--text);
101
+ --vp-badge-info-border: var(--border);
102
+ --vp-badge-tip-border: var(--secondary);
103
+ --vp-badge-tip-text: var(--text);
104
+ --vp-badge-note-border: var(--secondary);
105
+ --vp-badge-note-text: var(--text);
106
+ --vp-badge-important-border: var(--accent);
107
+ --vp-badge-important-text: var(--text);
108
+ --vp-badge-warning-border: var(--warning-text);
109
+ --vp-badge-warning-text: var(--text);
110
+ --vp-badge-danger-border: var(--danger);
111
+ --vp-badge-danger-text: var(--text);
112
+
113
+ /* Code: inline code in body colour (links in code keep the accent); blocks on the surface, with the high-contrast syntax themes. */
114
+ --vp-code-color: var(--text);
115
+ --vp-code-bg: var(--lift);
116
+ --vp-code-block-bg: var(--surface);
117
+ --vp-code-block-color: var(--text);
118
+ --vp-code-line-highlight-color: var(--lift);
119
+ --vp-code-line-diff-add-color: color-mix(in srgb, var(--ok) 12%, var(--surface));
120
+ --vp-code-line-diff-remove-color: color-mix(in srgb, var(--danger) 12%, var(--surface));
121
+ --vp-code-line-warning-color: color-mix(in srgb, var(--warning-text) 12%, var(--surface));
122
+ --vp-code-line-error-color: color-mix(in srgb, var(--danger) 12%, var(--surface));
123
+
124
+ /* Home hero: the accent into the primary, both of which pass contrast on the page in every look. */
125
+ --vp-home-hero-name-color: transparent;
126
+ --vp-home-hero-name-background: linear-gradient(135deg, var(--accent), var(--primary));
127
+ --vp-home-hero-image-background-image: linear-gradient(135deg, var(--accent), var(--primary));
128
+ --vp-home-hero-image-filter: blur(var(--space-lg));
129
+ }
130
+
131
+ /* Text enlarged to 200% on a wide screen: the doc columns are laid out in pixels while their content grows, so the main column kept its
132
+ desktop width and the page scrolled sideways. Letting the flex children of the doc container shrink (and long lines wrap or scroll inside
133
+ their own blocks) keeps the page at the viewport's width. */
134
+ .VPDoc .container,
135
+ .VPDoc .container > *,
136
+ .VPDoc .content-container,
137
+ .VPDoc .content {
138
+ min-width: 0;
139
+ }
140
+ .VPDoc .container {
141
+ max-width: 100%;
142
+ }
143
+
144
+ /* A feature card on the home page is one link around a title, a description and an action. Links are underlined (a colour is never the only
145
+ cue), but underlining the whole paragraph makes the card unreadable: the title and the action carry the underline. */
146
+ a.VPFeature {
147
+ text-decoration: none;
148
+ }
149
+ a.VPFeature .title,
150
+ a.VPFeature .link-text {
151
+ text-decoration: underline;
152
+ }