Type to search pages and sections.

Skip to content
Ginko

Theming and overrides

Colours, fonts and sizes of the docs layer through tokens, the two colour presets, light and dark, your own parts at the override points, and your own pages in the docs frame.

Change tokens first. Replace a part only when the tokens cannot give you the result. Everything on this page is public and stays stable between releases.

Pick a preset

theme in app.config.ts picks one of two colour presets:

ts
// app/app.config.ts
export default defineAppConfig({
  docs: { theme: 'green' },
})
PresetColours
defaultEmerald on zinc
greenGreen on slate

A preset sets defaults only. Every token your site sets wins over both.

Colours and fonts

The layer is plain CSS. Colours, radius and fonts are shadcn-style tokens, the same ones @lupinum/ginko-components reads:

--background, --foreground, --muted, --muted-foreground, --accent, --border, --primary, --primary-foreground, --card, --popover, --ring, --radius, --font-sans and --font-mono.

The layer's defaults sit in @layer theme with zero specificity. So every value your site sets wins, unlayered or through Tailwind 4's @theme:

css
/* app/assets/site.css */
:root {
  --primary: light-dark(#2f6f4f, #7cc59d);
  --primary-foreground: light-dark(#fff, #0c1f15);
  --font-sans: 'Inter', system-ui, sans-serif;
}
  • Set --primary-foreground together with --primary. A light colour such as yellow needs dark text; check the contrast in both modes.
  • Your site loads its own font before a token names it. The layer brings Inter and JetBrains Mono.
  • On a Tailwind 4 site that sets no --font-sans and --font-mono, Tailwind's fonts win.
  • Code blocks have tokens of their own (--ginko-code-bg and more), see Components.

Light and dark

The defaults are light-dark(light, dark) pairs. Without a choice the site follows the system. The switch in the header stores the reader's choice in localStorage (key docs-color-mode) and sets data-theme="light" or data-theme="dark" on <html>. A small script in the head applies it before the first paint, so nothing flickers.

A .dark class on <html>, for example from @nuxtjs/color-mode, works too. When your site sets its own values for both modes, write them as light-dark() pairs as well.

Sizes

The layout sizes are tokens too:

TokenDefaultWhat it sets
--docs-max90remWidth of the docs, gutters included
--docs-gutter1remSpace between the window edge and the docs
--docs-top0px (standalone: the header)Height of what sticks above the docs
--docs-sidebar17remWidth of the navigation
--docs-toc13.5remWidth of "On this page" beside the text (from 80rem window width)
--docs-measure38remReading width of running text
--docs-h1clamp(1.75rem, …, 2.25rem)Size of page titles
--docs-header4remHeight of the standalone header

Inside an existing site, --docs-max and --docs-top line the docs up with the rest of the site; more on Docs sites.

Override points

The layer ships two empty components. Your site replaces them with its own file of the same name in app/components/:

  • DocsPageEnd (prop doc, the page's entry): after the text of every page, before "Edit this page" and the pager. For a support box or a call to action.
  • DocsSidebarEnd: below the navigation, in the sidebar and in the phone menu. For a few links or a contact line.
vue
<!-- app/components/DocsPageEnd.vue -->
<script setup lang="ts">
defineProps<{ doc: { id: string } }>()
</script>

<template>
  <aside class="support">Questions? <a href="mailto:help@example.com">Write to us</a>.</aside>
</template>

Do not replace other Docs* components: they are internal and can change in any release. The layer's CSS classes are no contract either. Style through the tokens above.

Own pages and own layout

  • A page wins. When your site has a page at the same address, for example app/pages/index.vue, the layer adds none of its own there.
  • Your own page in the docs frame. <DocsShell> is the docs body: navigation, search and the phone bar. The default slot takes the page, the slot bar controls for the phone bar:
vue
<!-- app/pages/help/index.vue; the docs live at /help (docsCollection({ locales, base: '/help' })) -->
<script setup lang="ts">
const docs = useGinko('docs')
</script>

<template>
  <DocsShell>
    <h1>How can we help?</h1>
    <ul>
      <li v-for="item in docs.items" :key="item.id"><NuxtLink :to="item.url">{{ item.data.title }}</NuxtLink></li>
    </ul>
  </DocsShell>
</template>
  • Your own layout. With its own app/layouts/default.vue, your site keeps its header and footer, and the docs sit inside. <DocsShell> brings the search, above the navigation. You can also put the search, the languages and the light/dark switch into your own header: <DocsSearch />, <DocsLanguage />, <DocsThemeToggle />. With docs: { search: false } in app.config.ts the layer then leaves out its own search.
  • Texts. Every text of the layer is a message under docs.*, and your site's messages win key by key. A help center words the search for itself in i18n/en.json, for example: { "docs": { "searchPlaceholder": "Search help…" } }.

Do not copy the layer into your site. A copy no longer gets fixes, and it becomes unclear whether tokens, configuration or your code makes a decision.