From 521c59127f2bd1686a5e59bdc0c3a48b27203c4f Mon Sep 17 00:00:00 2001 From: Andrei Ivascu <7030530+aivascu@users.noreply.github.com> Date: Mon, 31 Aug 2026 22:44:17 +0300 Subject: [PATCH] Unify Shiki light/dark themes across landing, docs, and API. Override Nuxt UI's material light theme, use light-plus/dark-plus with defaultColor false everywhere, and apply class-based dual-theme CSS. --- site/app/assets/css/main.css | 46 +++++++++++++++++++----- site/nuxt.config.ts | 17 +++++++++ site/server/utils/highlightCode.ts | 48 ++++++++++++++++++-------- site/server/utils/renderApiMarkdown.ts | 11 ++---- 4 files changed, 92 insertions(+), 30 deletions(-) diff --git a/site/app/assets/css/main.css b/site/app/assets/css/main.css index 9a87948..77351e7 100644 --- a/site/app/assets/css/main.css +++ b/site/app/assets/css/main.css @@ -44,7 +44,7 @@ margin: 0; padding: 1rem; border-radius: 0; - background: transparent !important; + background: transparent; font-size: 0.875rem; line-height: 1.625; } @@ -53,15 +53,45 @@ font-size: inherit; } -/* Shiki dual themes — sync with Nuxt UI color mode (html.dark) */ -html:not(.dark) :where(.shiki, .shiki span) { - color: var(--shiki-light) !important; - background-color: var(--shiki-light-bg) !important; +/* + * Shiki dual themes (class-based), same theme names everywhere: + * light/default = light-plus, dark = dark-plus, defaultColor: false + * https://shiki.style/guide/dual-themes#class-based-dark-mode + */ +html .shiki { + background-color: var(--shiki-default-bg, var(--shiki-light-bg)); + color: var(--shiki-default, var(--shiki-light)); } -html.dark :where(.shiki, .shiki span) { - color: var(--shiki-dark) !important; - background-color: var(--shiki-dark-bg) !important; +html .shiki span { + color: var(--shiki-default, var(--shiki-light)); + font-style: var(--shiki-default-font-style, var(--shiki-light-font-style)); + font-weight: var(--shiki-default-font-weight, var(--shiki-light-font-weight)); + text-decoration: var(--shiki-default-text-decoration, var(--shiki-light-text-decoration)); +} + +html.light .shiki { + background-color: var(--shiki-light-bg, var(--shiki-default-bg)); + color: var(--shiki-light, var(--shiki-default)); +} + +html.light .shiki span { + color: var(--shiki-light, var(--shiki-default)); + font-style: var(--shiki-light-font-style, var(--shiki-default-font-style)); + font-weight: var(--shiki-light-font-weight, var(--shiki-default-font-weight)); + text-decoration: var(--shiki-light-text-decoration, var(--shiki-default-text-decoration)); +} + +html.dark .shiki { + background-color: var(--shiki-dark-bg); + color: var(--shiki-dark); +} + +html.dark .shiki span { + color: var(--shiki-dark); + font-style: var(--shiki-dark-font-style); + font-weight: var(--shiki-dark-font-weight); + text-decoration: var(--shiki-dark-text-decoration); } .prose-docs :where(code) { diff --git a/site/nuxt.config.ts b/site/nuxt.config.ts index 9db8b77..86a6a6c 100644 --- a/site/nuxt.config.ts +++ b/site/nuxt.config.ts @@ -80,7 +80,9 @@ export default defineNuxtConfig({ build: { markdown: { highlight: { + // Must set `light` too — @nuxt/ui defaults it to material-theme-lighter. theme: { + light: 'light-plus', default: 'light-plus', dark: 'dark-plus', }, @@ -92,10 +94,25 @@ export default defineNuxtConfig({ 'json', 'yaml', 'md', + 'js', + 'ts', + 'html', + 'css', + 'vue', ], }, }, }, }, + // Explicit MDC override (Nuxt UI registers highlight defaults on @nuxtjs/mdc). + mdc: { + highlight: { + theme: { + light: 'light-plus', + default: 'light-plus', + dark: 'dark-plus', + }, + }, + }, compatibilityDate: '2025-08-01', }) diff --git a/site/server/utils/highlightCode.ts b/site/server/utils/highlightCode.ts index e8c62e1..361c43d 100644 --- a/site/server/utils/highlightCode.ts +++ b/site/server/utils/highlightCode.ts @@ -1,42 +1,62 @@ import { createHighlighter, type Highlighter } from 'shiki' -const SHIKI_LIGHT_THEME = 'light-plus' -const SHIKI_DARK_THEME = 'dark-plus' - -const LANGS = ['csharp', 'json', 'js', 'ts', 'html', 'css', 'vue', 'shell', 'bash', 'xml', 'yaml', 'md', 'text'] as const +/** Same theme keys as Nuxt Content / @nuxtjs/mdc (defaultColor: false). */ +export const SHIKI_THEME_LIGHT = 'light-plus' +export const SHIKI_THEME_DARK = 'dark-plus' + +export const SHIKI_THEMES = { + // Include `light` so we override Nuxt UI's MDC default (material-theme-lighter). + light: SHIKI_THEME_LIGHT, + default: SHIKI_THEME_LIGHT, + dark: SHIKI_THEME_DARK, +} as const + +export const SHIKI_LANGS = [ + 'csharp', + 'json', + 'js', + 'ts', + 'html', + 'css', + 'vue', + 'shell', + 'bash', + 'xml', + 'yaml', + 'md', + 'text', +] as const + +export type ShikiLang = (typeof SHIKI_LANGS)[number] let highlighterPromise: Promise | null = null async function getHighlighter() { if (!highlighterPromise) { highlighterPromise = createHighlighter({ - themes: [SHIKI_LIGHT_THEME, SHIKI_DARK_THEME], - langs: [...LANGS], + themes: [SHIKI_THEME_LIGHT, SHIKI_THEME_DARK], + langs: [...SHIKI_LANGS], }) } return highlighterPromise } -function normalizeLang(lang: string) { +export function normalizeLang(lang: string): ShikiLang { const value = lang.trim().toLowerCase() if (value === 'cs') return 'csharp' if (value === 'csproj') return 'xml' - return LANGS.includes(value as typeof LANGS[number]) ? value : 'text' + return SHIKI_LANGS.includes(value as ShikiLang) ? (value as ShikiLang) : 'text' } export async function getCodeHighlighter() { return getHighlighter() } -export { normalizeLang } - export async function highlightCode(code: string, lang = 'text') { const highlighter = await getHighlighter() return highlighter.codeToHtml(code.trimEnd(), { lang: normalizeLang(lang), - themes: { - light: SHIKI_LIGHT_THEME, - dark: SHIKI_DARK_THEME, - }, + themes: { ...SHIKI_THEMES }, + defaultColor: false, }) } diff --git a/site/server/utils/renderApiMarkdown.ts b/site/server/utils/renderApiMarkdown.ts index ffba475..e550131 100644 --- a/site/server/utils/renderApiMarkdown.ts +++ b/site/server/utils/renderApiMarkdown.ts @@ -1,8 +1,5 @@ import MarkdownIt from 'markdown-it' -import { getCodeHighlighter, normalizeLang } from './highlightCode' - -const SHIKI_LIGHT_THEME = 'light-plus' -const SHIKI_DARK_THEME = 'dark-plus' +import { getCodeHighlighter, normalizeLang, SHIKI_THEMES } from './highlightCode' function stripHtml(value: string) { return value @@ -74,10 +71,8 @@ export async function renderApiMarkdown(markdown: string) { const code = token.content.replace(/\n$/, '') const html = highlighter.codeToHtml(code, { lang, - themes: { - light: SHIKI_LIGHT_THEME, - dark: SHIKI_DARK_THEME, - }, + themes: { ...SHIKI_THEMES }, + defaultColor: false, }) return `
${html}
`