Style and Layout
Trellis Docs includes custom theme components and a design token system that give you full control over your site's look and feel.
Theme components
Trellis Docs uses fully custom theme components. This gives complete control over every aspect of rendering — from where the last-updated date appears to what icons admonitions use.
Directory structure
components/docs/
├── mdx/
│ ├── index.tsx # MDX component registry
│ ├── callout.tsx # Admonitions (note, tip, info, caution, danger) with custom SVG icons
│ ├── code-block.tsx # Shiki syntax highlighting (server component)
│ ├── code-block-client.tsx # Copy button + word wrap toggle (client component)
│ ├── heading.tsx # Heading anchors with copy-to-clipboard
│ ├── tabs.tsx # Pill-style tabs with URL sync
│ ├── image-lightbox.tsx # Click-to-zoom image modal
│ └── mermaid.tsx # Mermaid diagram renderer with pan/zoom
├── search/
│ └── search-dialog.tsx # Cmd+K search modal (Fuse.js)
├── navbar.tsx # Top navbar + mobile sidebar drawer
├── sidebar.tsx # Desktop sidebar with collapsible categories
├── breadcrumbs.tsx # Breadcrumb navigation
├── toc.tsx # Table of contents (right sidebar)
└── footer.tsx # Site footerModifying theme components
To customize a component, edit it directly in components/docs/.
Before making changes, read the existing component code to understand what it does.
Since these are custom components, they are maintained within the Trellis project. When upgrading Next.js, check for any breaking changes that might affect theme components.
Last-updated placement
In many documentation frameworks, the last-updated date and "Edit this page" link appear at the bottom of the page, in the footer area. Trellis Docs moves them to the top, directly below the page title.
Why top placement?
Readers want to know immediately whether the content they're reading is current. Placing the date at the top gives instant context about freshness — no scrolling required. This is especially important for documentation that changes frequently, like API references or configuration guides.
Configuration
The date and author are set per-page via frontmatter:
---
last_update:
date: 02/20/2026
author: Your Name
---The author field is useful as a subject matter expert reference for contributors. To control whether the author name is displayed on the page, toggle showAuthor in config/site.ts:
lastUpdated: {
showAuthor: false, // true to display the author next to the date
},When showAuthor is false (the default), only the date renders. The author remains in the frontmatter for contributor reference.
Design tokens
Trellis Docs uses a design token system that converts a single JSON file into CSS custom properties. This gives you a centralized place to define your brand colors, spacing, typography, and other design values.
How it works
design-tokens.json → build-tokens.js → app/tokens.css → globals.cssdesign-tokens.jsondefines tokens as nested objects withvalueandtypepropertiesscripts/build-tokens.jsreads the JSON and generates a:root {}CSS blockapp/tokens.cssis the generated output (do not edit manually)app/globals.cssmaps token variables to CSS variables
The script runs automatically before npm run dev and npm run build (via the prebuild and predev npm scripts).
Token file structure
{
"brand": {
"primary-600": {
"value": "#7c3aed",
"type": "color"
}
},
"accent": {
"teal-400": {
"value": "#2dd4bf",
"type": "color"
}
},
"spacing": {
"spacing-16": {
"value": "16px",
"type": "spacing"
}
}
}Each token has:
value— the actual CSS valuetype— a descriptor (used for documentation; not consumed by the build script)
Generated CSS
The build script converts nested keys into CSS custom property names using -- prefix and - separators:
:root {
--brand-primary-600-value: #7c3aed;
--accent-teal-400-value: #2dd4bf;
--spacing-spacing-16-value: 16px;
}Token categories
The default design-tokens.json includes these categories:
| Category | Tokens | Description |
|---|---|---|
neutral | white, slate-50 through slate-900 | Background and text colors |
brand | primary-50 through primary-900 | Primary brand color scale (violet) |
accent | teal-200 through teal-600 | Accent color scale (teal) |
utility | green-20/100, red-20/100, yellow-20/100 | Status/feedback colors |
spacing | spacing-8 through spacing-120 | Spacing values in pixels |
border-radius | border-radius-4/8/30/900 | Border radius values |
border-width | border-width-1/2/none | Border width values |
typography | fontFamily | Font family stack |
Using tokens in CSS
Reference generated tokens in globals.css or any CSS module:
.my-component {
color: var(--brand-primary-600-value);
background: var(--neutral-slate-50-value);
padding: var(--spacing-spacing-16-value);
border-radius: var(--border-radius-border-radius-8-value);
}Mapping to theme variables
globals.css maps tokens to theme CSS variables so the entire theme responds to token changes:
:root {
--ifm-color-primary: var(--brand-primary-600-value);
--ifm-color-primary-dark: var(--brand-primary-700-value);
--ifm-color-primary-light: var(--brand-primary-500-value);
--ifm-font-family-base: var(--typography-font-family-value);
}Customizing design tokens
Changing brand colors
Open design-tokens.json and update the brand section. The default uses a navy blue scale:
{
"brand": {
"primary-50": { "value": "#f0f5fa", "type": "color" },
"primary-100": { "value": "#dce8f2", "type": "color" },
"primary-200": { "value": "#b3cde0", "type": "color" },
"primary-300": { "value": "#8bb3cf", "type": "color" },
"primary-400": { "value": "#7ba4c4", "type": "color" },
"primary-500": { "value": "#4a7da8", "type": "color" },
"primary-600": { "value": "#2a5a82", "type": "color" },
"primary-700": { "value": "#1a3b5c", "type": "color" },
"primary-800": { "value": "#122a43", "type": "color" },
"primary-900": { "value": "#0b1c2e", "type": "color" }
}
}Replace the hex values with your brand's color scale. Use a tool like Tailwind CSS Colors or Coolors to generate a consistent scale.
After editing, regenerate the CSS:
npm run build-tokensOr restart the dev server — it runs build-tokens automatically:
npm run devChanging accent colors
The accent section controls secondary colors used for interactive elements like links and highlights in dark mode:
{
"accent": {
"olive-200": { "value": "#dde896", "type": "color" },
"olive-300": { "value": "#c8d96e", "type": "color" },
"olive-400": { "value": "#b4ca4a", "type": "color" },
"olive-500": { "value": "#a2b53a", "type": "color" },
"olive-600": { "value": "#8a9a2f", "type": "color" }
}
}When choosing accent colors, ensure sufficient contrast against both light and dark backgrounds. The 400 shade is used most often.
Adding tokens
Add categories or tokens by extending the JSON structure:
{
"brand": {
"primary-600": { "value": "#2a5a82", "type": "color" },
"secondary-600": { "value": "#2563eb", "type": "color" }
}
}The build script generates:
--color-brand-secondary-600: #2563eb;You can then use it in app/globals.css or any component:
.my-element {
color: var(--color-brand-secondary-600);
}Changing typography
Update the font family in the typography section:
{
"typography": {
"fontFamily": {
"name": "font-family",
"value": "'Inter', 'Segoe UI', system-ui, sans-serif"
}
}
}If using a web font, add the import in app/globals.css:
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap');Replacing the logo
Logos are configured via paths in config/site.ts — there's no React component to edit. Drop your image files into public/img/ and update the paths:
-
Place your logo images in
public/img/:- Navbar logo — small mark, ~32px tall (e.g.
my-logo-small.svg) - Hero logo — larger version for the landing page (e.g.
my-logo-hero.svg)
- Navbar logo — small mark, ~32px tall (e.g.
-
Update
config/site.ts:
logo: {
navbar: '/img/my-logo-small.svg',
hero: '/img/my-logo-hero.svg',
alt: 'My Company Logo',
},Set either field to null to hide that logo. The navbar still renders the site title even when navbar is null.
SVGs rendered via <img> don't inherit currentColor, so theme switching with a single SVG is limited. If you need different logos for light and dark modes, ship two SVGs and toggle in CSS, or use a fixed neutral color that reads well in both themes.
Full rebranding checklist
- Colors — Update
brandandaccentsections indesign-tokens.json. - Typography — Update
typography.fontFamily(add web font import if needed). - Logo — Drop your image files into
public/img/and update thelogopaths inconfig/site.ts. - Config — Update
title,tagline,url,repoUrl, andlogoinconfig/site.ts. - Navigation — Update links in
config/navigation.tsand copyright infooterConfig. - Regenerate — Run
npm run build-tokens. - Verify — Run
npm run buildand check for errors, thennpm run devfor visual review.
Token naming conventions
Follow these conventions for consistency:
- Colors:
{category}-{shade}→primary-600,olive-400 - Spacing:
spacing-{pixels}→spacing-16,spacing-24 - Border radius:
border-radius-{pixels}→border-radius-8 - Border width:
border-width-{pixels}→border-width-1
The build script converts these to CSS variables by joining the full path with hyphens: --color-brand-primary-600, --spacing-spacing-16.