Theme and override the layer
Change design tokens or replace a focused Nuxt page, layout, or shell component without copying the layer.
Change tokens first. Replace a component only when the existing structure cannot express the result you need.
Select a built-in palette
Choose neutral surfaces and the primary action color independently in app config:
export default defineAppConfig({
ginkoDocs: {
theme: {
neutral: "mauve",
primary: "violet",
},
},
});Use primary: 'neutral' when primary controls should follow the neutral palette. Palette selection changes shadcn semantic tokens; status colors and landing-page brand accents remain independent.
Define custom palettes
Set either palette to custom, then define its palette variables in the consumer theme CSS. A custom neutral is a complete ramp:
export default defineAppConfig({
ginkoDocs: {
theme: { neutral: "custom", primary: "custom" },
},
});html[data-neutral="custom"] {
--theme-neutral-50: oklch(0.985 0.004 250);
--theme-neutral-100: oklch(0.965 0.008 250);
--theme-neutral-200: oklch(0.92 0.012 250);
--theme-neutral-300: oklch(0.87 0.018 250);
--theme-neutral-400: oklch(0.71 0.025 250);
--theme-neutral-500: oklch(0.55 0.03 250);
--theme-neutral-600: oklch(0.45 0.03 250);
--theme-neutral-700: oklch(0.37 0.028 250);
--theme-neutral-800: oklch(0.28 0.024 250);
--theme-neutral-900: oklch(0.21 0.02 250);
--theme-neutral-950: oklch(0.14 0.012 250);
}A custom primary defines explicit colors, foregrounds, and focus rings for both color modes:
html[data-primary="custom"] {
--theme-primary-light: oklch(0.52 0.2 255);
--theme-primary-light-foreground: oklch(0.985 0.004 255);
--theme-primary-light-ring: oklch(0.62 0.17 255);
--theme-primary-dark: oklch(0.72 0.14 255);
--theme-primary-dark-foreground: oklch(0.18 0.025 255);
--theme-primary-dark-ring: oklch(0.72 0.14 255);
}Define the foregrounds instead of assuming white text; light colors such as yellow or lime need a dark foreground. Missing custom variables fall back individually to the Zinc and neutral defaults.
Load theme CSS after the layer
Import consumer theme CSS from a Nuxt plugin. The plugin import is added after the layer styles without replacing Nuxt's merged css array.
import "~/assets/css/theme.css";
export default defineNuxtPlugin(() => {});Override the semantic variables used by the shell and content components:
:root {
--font-body: Inter, system-ui, sans-serif;
--font-heading: Inter, system-ui, sans-serif;
--radius: 0.5rem;
--background: oklch(0.99 0.004 255);
--foreground: oklch(0.19 0.025 255);
--primary: oklch(0.51 0.19 258);
--primary-foreground: oklch(0.99 0.004 255);
--muted: oklch(0.96 0.01 255);
--muted-foreground: oklch(0.48 0.03 255);
--border: oklch(0.9 0.015 255);
--ring: oklch(0.62 0.16 258);
}
.dark {
--background: oklch(0.16 0.02 255);
--foreground: oklch(0.96 0.008 255);
--primary: oklch(0.73 0.14 258);
--primary-foreground: oklch(0.15 0.02 255);
--muted: oklch(0.23 0.025 255);
--muted-foreground: oklch(0.72 0.025 255);
--border: oklch(1 0 0 / 12%);
--ring: oklch(0.73 0.14 258);
}Keep foreground and background pairs readable in both color modes. Add the chosen web fonts to the app before referencing them in a token.
Replace one shell component
Nuxt's layer override rules let the consumer replace a stable shell component with a component of the same name. Ginko Docs exposes these focused seams:
SiteHeader,SiteFooter,SiteBanner, andSiteLogoMark;SiteInteractionLayerandDocsSidebar;SiteLocaleSwitcherwhen the complete language-control UI needs a custom implementation.
For example, create app/components/SiteFooter.vue to replace only the footer:
<template>
<footer class="border-t border-border px-6 py-8 text-sm text-muted-foreground">
<p>© {{ new Date().getFullYear() }} Acme</p>
</footer>
</template>Configure light and dark logo paths in app.config.ts when only the mark changes; a component override is unnecessary for that case.
Replace a page or layout
Create the same Nuxt path in the consumer to replace a layer page or layout:
- app
- layouts
- docs.vuedocumentation shell
- pages
- index.vuelanding page
An app/pages/index.vue file takes ownership of the landing page. An app/layouts/docs.vue file takes ownership of the documentation shell. A layout replacement must preserve accessible landmarks, the skip target, responsive navigation, and a place for <slot />.
Do not copy the complete layer into the application. A copied tree stops receiving fixes and makes it unclear whether configuration, tokens, or consumer code owns a visual decision.