From 9ecdc4e0839d98fd591f76412548c52938b83dbc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Chopin?= Date: Thu, 7 May 2026 12:48:45 +0200 Subject: [PATCH 1/5] feat: add support for `app/app.css` --- docs/app/app.css | 3 ++ docs/app/assets/css/main.css | 7 ---- .../1.getting-started/4.project-structure.md | 3 +- docs/content/en/2.concepts/4.theme.md | 20 +++-------- .../1.getting-started/4.project-structure.md | 1 + docs/content/fr/2.concepts/4.theme.md | 18 +++------- docs/nuxt.config.ts | 1 - layer/app/plugins/i18n.ts | 2 +- layer/modules/assistant/index.ts | 6 ++-- layer/modules/config.ts | 2 +- layer/modules/css.ts | 33 +++++++++++++++---- layer/modules/markdown-rewrite.ts | 2 +- layer/modules/skills/index.ts | 6 ++-- layer/package.json | 1 + pnpm-lock.yaml | 3 ++ 15 files changed, 57 insertions(+), 51 deletions(-) create mode 100644 docs/app/app.css delete mode 100644 docs/app/assets/css/main.css diff --git a/docs/app/app.css b/docs/app/app.css new file mode 100644 index 000000000..9280e14ad --- /dev/null +++ b/docs/app/app.css @@ -0,0 +1,3 @@ +@theme { + --font-sans: "Public Sans", sans-serif; +} diff --git a/docs/app/assets/css/main.css b/docs/app/assets/css/main.css deleted file mode 100644 index 6c5225ab1..000000000 --- a/docs/app/assets/css/main.css +++ /dev/null @@ -1,7 +0,0 @@ -@import "tailwindcss"; -@import "@nuxt/ui"; - -@theme { - --font-sans: 'Public Sans', sans-serif; - --ui-container: 90rem; -} \ No newline at end of file diff --git a/docs/content/en/1.getting-started/4.project-structure.md b/docs/content/en/1.getting-started/4.project-structure.md index 628377017..a8b771d8d 100644 --- a/docs/content/en/1.getting-started/4.project-structure.md +++ b/docs/content/en/1.getting-started/4.project-structure.md @@ -140,7 +140,8 @@ You need a `nuxt.config.ts` to be set if you want to override your app with Nuxt ```bash my-docs/ ├── app/ # App directory (optional) - ├── app.config.ts # App configuration +│ ├── app.config.ts # App configuration +│ ├── app.css # Custom theme (auto-imported by Docus) │ ├── components/ # Custom Vue components │ ├── layouts/ # Custom layouts │ ├── pages/ # Custom Vue pages (outside of content) diff --git a/docs/content/en/2.concepts/4.theme.md b/docs/content/en/2.concepts/4.theme.md index c293215d8..4b536d9fb 100644 --- a/docs/content/en/2.concepts/4.theme.md +++ b/docs/content/en/2.concepts/4.theme.md @@ -13,17 +13,11 @@ For a full overview of Nuxt UI theming, check out the Nuxt UI documentation. ## Override with `@theme` -You can customize your theme with CSS variables inside a `@theme` directive to define your project's custom design tokens, like fonts, colors, and breakpoints +You can customize your theme with CSS variables inside a `@theme` directive to define your project's custom design tokens, like fonts, colors, and breakpoints. -You can override this theme by creating a `main.css` file in your application. - -This way you can override your theme: - -::code-group -```css [assets/css/main.css] -@import "tailwindcss"; -@import "@nuxt/ui"; +To override the theme, create an `app/app.css` file in your project: +```css [app/app.css] @theme { --font-sans: 'Public Sans', sans-serif; @@ -43,12 +37,8 @@ This way you can override your theme: } ``` -```ts [nuxt.config.ts] -export default defineNuxtConfig({ - modules: ['@nuxt/ui'], - css: ['~/assets/css/main.css'] -}) -``` +::note +Docus automatically imports `app/app.css` — you don't need to add it to the `css` array in `nuxt.config.ts`, and you should **not** include `@import "tailwindcss"` in this file as Docus handles it for you. :: ## Colors diff --git a/docs/content/fr/1.getting-started/4.project-structure.md b/docs/content/fr/1.getting-started/4.project-structure.md index 1619fc710..255743bcf 100644 --- a/docs/content/fr/1.getting-started/4.project-structure.md +++ b/docs/content/fr/1.getting-started/4.project-structure.md @@ -140,6 +140,7 @@ Un fichier `nuxt.config.ts` doit être existant pour surcharger votre app Nuxt. my-docs/ ├── app/ # Répertoire app (optionnel) │ ├── app.config.ts # App configuration +│ ├── app.css # Thème personnalisé (auto-importé par Docus) │ ├── components/ # Composants Vue personnalisés │ ├── layouts/ # Layouts personnalisés │ ├── pages/ # Pages Vue personnalisées (en dehors du contenu) diff --git a/docs/content/fr/2.concepts/4.theme.md b/docs/content/fr/2.concepts/4.theme.md index 90893e489..5e2d1c287 100644 --- a/docs/content/fr/2.concepts/4.theme.md +++ b/docs/content/fr/2.concepts/4.theme.md @@ -15,15 +15,9 @@ Pour un aperçu complet du système de thème Nuxt UI, consultez la documentatio Vous pouvez personnaliser votre thème avec des variables CSS à l'intérieur d'une directive `@theme` pour définir les tokens de design personnalisés de votre projet, comme les polices, couleurs et breakpoints. -Vous pouvez surcharger ce thème en créant un fichier `main.css` dans votre application. - -De cette façon, vous pouvez surcharger votre thème : - -::code-group -```css [assets/css/main.css] -@import "tailwindcss"; -@import "@nuxt/ui"; +Pour surcharger le thème, créez un fichier `app/app.css` dans votre projet : +```css [app/app.css] @theme { --font-sans: 'Public Sans', sans-serif; @@ -43,12 +37,8 @@ De cette façon, vous pouvez surcharger votre thème : } ``` -```ts [nuxt.config.ts] -export default defineNuxtConfig({ - modules: ['@nuxt/ui'], - css: ['~/assets/css/main.css'] -}) -``` +::note +Docus importe automatiquement `app/app.css` — vous n'avez pas besoin de l'ajouter au tableau `css` dans `nuxt.config.ts`, et vous ne devez **pas** inclure `@import "tailwindcss"` dans ce fichier car Docus s'en charge pour vous. :: ## Couleurs diff --git a/docs/nuxt.config.ts b/docs/nuxt.config.ts index 5c548acd9..752f14bb6 100644 --- a/docs/nuxt.config.ts +++ b/docs/nuxt.config.ts @@ -1,7 +1,6 @@ export default defineNuxtConfig({ extends: ['docus'], modules: ['@nuxtjs/i18n', 'nuxt-studio'], - css: ['~/assets/css/main.css'], site: { name: 'Docus', }, diff --git a/layer/app/plugins/i18n.ts b/layer/app/plugins/i18n.ts index 828bd8473..1d568b72d 100644 --- a/layer/app/plugins/i18n.ts +++ b/layer/app/plugins/i18n.ts @@ -1,7 +1,7 @@ import type { RouteLocationNormalized } from 'vue-router' import { consola } from 'consola' -const log = consola.withTag('Docus') +const log = consola.withTag('docus') // Lazy import functions for locale files (bundled but not eagerly loaded) const localeFiles = import.meta.glob<{ default: Record }>('../../i18n/locales/*.json') diff --git a/layer/modules/assistant/index.ts b/layer/modules/assistant/index.ts index 11522d12e..f3c19aa4e 100644 --- a/layer/modules/assistant/index.ts +++ b/layer/modules/assistant/index.ts @@ -21,7 +21,7 @@ export interface AssistantModuleOptions { model?: string } -const log = logger.withTag('Docus') +const log = logger.withTag('docus') const defaults: Required = { apiPath: '/__docus__/assistant', @@ -77,7 +77,9 @@ export default defineNuxtModule({ ) if (!hasAiGatewayAuth) { - log.warn('AI assistant disabled: neither AI_GATEWAY_API_KEY nor VERCEL_OIDC_TOKEN found') + nuxt.hook('modules:done', () => { + log.warn('AI assistant disabled: neither `AI_GATEWAY_API_KEY` nor `VERCEL_OIDC_TOKEN` found') + }) return } diff --git a/layer/modules/config.ts b/layer/modules/config.ts index 6fdde4017..8582f3c16 100644 --- a/layer/modules/config.ts +++ b/layer/modules/config.ts @@ -5,7 +5,7 @@ import { join } from 'node:path' import { inferSiteURL, getPackageJsonMetadata } from '../utils/meta' import { getGitBranch, getGitEnv, getLocalGitInfo } from '../utils/git' -const log = logger.withTag('Docus') +const log = logger.withTag('docus') type I18nLocale = string | { code: string, name?: string } type DocusI18nOptions = { locales?: I18nLocale[], strategy?: string } diff --git a/layer/modules/css.ts b/layer/modules/css.ts index 818b9b332..066fcce88 100644 --- a/layer/modules/css.ts +++ b/layer/modules/css.ts @@ -1,21 +1,36 @@ -import { defineNuxtModule, addTemplate, createResolver } from '@nuxt/kit' -import { joinURL } from 'ufo' +import { defineNuxtModule, addTemplate, createResolver, logger } from '@nuxt/kit' +import { existsSync } from 'fs' +import { readFile } from 'fs/promises' +import { resolve } from 'pathe' import { resolveModulePath } from 'exsolve' +const log = logger.withTag('docus') + export default defineNuxtModule({ meta: { - name: 'css', + name: 'docus-css', }, async setup(_options, nuxt) { - const dir = nuxt.options.rootDir const resolver = createResolver(import.meta.url) - const contentDir = joinURL(dir, 'content') + const contentDir = resolve(nuxt.options.rootDir, 'content') const uiPath = resolveModulePath('@nuxt/ui', { from: import.meta.url, conditions: ['style'] }) const tailwindPath = resolveModulePath('tailwindcss', { from: import.meta.url, conditions: ['style'] }) const layerDir = resolver.resolve('../app') const assistantDir = resolver.resolve('../modules/assistant') + let userDocusPath = resolve(nuxt.options.srcDir, 'app.css') + if (existsSync(userDocusPath)) { + const userDocusCss = await readFile(userDocusPath, 'utf-8') + if (userDocusCss.includes('@import "tailwindcss"')) { + nuxt.hook('modules:done', () => { + log.warn('`app.css` contains `@import "tailwindcss";` consider removing it to avoid duplicate css.') + }) + } + } else { + userDocusPath = null + } + const cssTemplate = addTemplate({ filename: 'docus.css', getContents: () => { @@ -25,7 +40,13 @@ export default defineNuxtModule({ @source "${contentDir.replace(/\\/g, '/')}/**/*"; @source "${layerDir.replace(/\\/g, '/')}/**/*"; @source "../../app.config.ts"; -@source "${assistantDir.replace(/\\/g, '/')}/**/*";` +@source "${assistantDir.replace(/\\/g, '/')}/**/*"; + +@layer base { + :root { + --ui-container: 90rem; + } +}` + (userDocusPath ? `\n@import ${JSON.stringify(userDocusPath)};` : '') }, }) diff --git a/layer/modules/markdown-rewrite.ts b/layer/modules/markdown-rewrite.ts index 648da8d56..45839aaf7 100644 --- a/layer/modules/markdown-rewrite.ts +++ b/layer/modules/markdown-rewrite.ts @@ -2,7 +2,7 @@ import { defineNuxtModule, logger } from '@nuxt/kit' import { resolve } from 'node:path' import { readFile, writeFile } from 'node:fs/promises' -const log = logger.withTag('Docus') +const log = logger.withTag('docus') type I18nLocale = string | { code: string } type DocusI18nOptions = { locales?: I18nLocale[] } diff --git a/layer/modules/skills/index.ts b/layer/modules/skills/index.ts index e03b8370d..d6b41825c 100644 --- a/layer/modules/skills/index.ts +++ b/layer/modules/skills/index.ts @@ -19,7 +19,7 @@ export interface SkillsModuleOptions { const SKILL_NAME_REGEX = /^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/ const MAX_NAME_LENGTH = 64 -const log = logger.withTag('Docus') +const log = logger.withTag('docus') const defaults: Required = { dir: 'skills', @@ -38,7 +38,9 @@ export default defineNuxtModule({ const catalog = await scanSkills(skillsDir) if (!catalog.length) return - log.info(`Found ${catalog.length} agent skill${catalog.length > 1 ? 's' : ''}: ${catalog.map(s => s.name).join(', ')}`) + nuxt.hook('modules:done', () => { + log.info(`Found ${catalog.length} agent skill${catalog.length > 1 ? 's' : ''}: ${catalog.map(s => s.name).join(', ')}`) + }) nuxt.options.runtimeConfig.skills = { catalog } diff --git a/layer/package.json b/layer/package.json index 26726c36f..e63cd9bd5 100644 --- a/layer/package.json +++ b/layer/package.json @@ -50,6 +50,7 @@ "motion-v": "^2.2.1", "nuxt-llms": "^0.2.0", "nuxt-og-image": "^6.4.5", + "pathe": "^2.0.3", "pkg-types": "^2.3.0", "scule": "^1.3.0", "shiki-stream": "^0.1.4", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f908be671..cfdec61a9 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -192,6 +192,9 @@ importers: nuxt-og-image: specifier: ^6.4.5 version: 6.4.5(b2d783c839050aabda43ec2f68abe3b0) + pathe: + specifier: ^2.0.3 + version: 2.0.3 pkg-types: specifier: ^2.3.0 version: 2.3.0 From dd6b91caedecda3db8de550cd93a807b38980af2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Chopin?= Date: Thu, 7 May 2026 12:53:27 +0200 Subject: [PATCH 2/5] lint fix --- layer/modules/css.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/layer/modules/css.ts b/layer/modules/css.ts index 066fcce88..f038ade9f 100644 --- a/layer/modules/css.ts +++ b/layer/modules/css.ts @@ -1,6 +1,6 @@ import { defineNuxtModule, addTemplate, createResolver, logger } from '@nuxt/kit' -import { existsSync } from 'fs' -import { readFile } from 'fs/promises' +import { existsSync } from 'node:fs' +import { readFile } from 'node:fs/promises' import { resolve } from 'pathe' import { resolveModulePath } from 'exsolve' @@ -27,7 +27,8 @@ export default defineNuxtModule({ log.warn('`app.css` contains `@import "tailwindcss";` consider removing it to avoid duplicate css.') }) } - } else { + } + else { userDocusPath = null } From 6b262140d9a115e02ef4d3568356e2410b7fc267 Mon Sep 17 00:00:00 2001 From: Baptiste Leproux Date: Thu, 7 May 2026 12:55:36 +0200 Subject: [PATCH 3/5] Update docs/content/en/2.concepts/4.theme.md --- docs/content/en/2.concepts/4.theme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/content/en/2.concepts/4.theme.md b/docs/content/en/2.concepts/4.theme.md index 4b536d9fb..2f3d93bdc 100644 --- a/docs/content/en/2.concepts/4.theme.md +++ b/docs/content/en/2.concepts/4.theme.md @@ -37,7 +37,7 @@ To override the theme, create an `app/app.css` file in your project: } ``` -::note +::warning Docus automatically imports `app/app.css` — you don't need to add it to the `css` array in `nuxt.config.ts`, and you should **not** include `@import "tailwindcss"` in this file as Docus handles it for you. :: From 00668684ae0200c7d6bf2f24500236fff8d46fc4 Mon Sep 17 00:00:00 2001 From: Baptiste Leproux Date: Thu, 7 May 2026 12:55:41 +0200 Subject: [PATCH 4/5] Update docs/content/fr/2.concepts/4.theme.md --- docs/content/fr/2.concepts/4.theme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/content/fr/2.concepts/4.theme.md b/docs/content/fr/2.concepts/4.theme.md index 5e2d1c287..da26e75cb 100644 --- a/docs/content/fr/2.concepts/4.theme.md +++ b/docs/content/fr/2.concepts/4.theme.md @@ -37,7 +37,7 @@ Pour surcharger le thème, créez un fichier `app/app.css` dans votre projet : } ``` -::note +::warning Docus importe automatiquement `app/app.css` — vous n'avez pas besoin de l'ajouter au tableau `css` dans `nuxt.config.ts`, et vous ne devez **pas** inclure `@import "tailwindcss"` dans ce fichier car Docus s'en charge pour vous. :: From 7f359bf759ee2c56dde469ea819f0ee021bbc11a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Chopin?= Date: Thu, 7 May 2026 12:58:33 +0200 Subject: [PATCH 5/5] Update css.ts --- layer/modules/css.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/layer/modules/css.ts b/layer/modules/css.ts index f038ade9f..0c50e23c8 100644 --- a/layer/modules/css.ts +++ b/layer/modules/css.ts @@ -19,7 +19,7 @@ export default defineNuxtModule({ const layerDir = resolver.resolve('../app') const assistantDir = resolver.resolve('../modules/assistant') - let userDocusPath = resolve(nuxt.options.srcDir, 'app.css') + let userDocusPath: string | null = resolve(nuxt.options.srcDir, 'app.css') if (existsSync(userDocusPath)) { const userDocusCss = await readFile(userDocusPath, 'utf-8') if (userDocusCss.includes('@import "tailwindcss"')) {