Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Docship Theme Development Checklist

⚠️ Critical Rules

No Advertisements Policy

  • NEVER use any theme or plugin that contains ads
  • Some official themes (e.g., @vuepress/theme-vue) contain sponsor/advertisement content - DO NOT USE
  • Always check if theme contains ads, sponsor links, or donation prompts before using
  • When in doubt, use the framework’s default theme

Known themes/plugins with ads (DO NOT USE):

FrameworkTheme/PluginIssue
VuePress@vuepress/theme-vueContains sponsor/advertisement content

Recommended ad-free alternatives:

FrameworkRecommended Theme
VuePressDefault theme (built-in @vuepress/theme-default)

User Content Respect

  • Never restrict user filenames (e.g., index.md, temp.md are all valid)
  • User markdown content must never break builds regardless of validity
  • Invalid image paths, links, etc. should be ignored, not cause errors

Markdown Compatibility

  • Use markdown.format: 'md' instead of MDX to avoid JSX parsing errors (Docusaurus)
    • Users may have ${}, <>, {} in their markdown content
    • MDX will treat these as JSX expressions and fail
  • VitePress is more lenient with ${} but double curly braces will be treated as Vue template
  • Don’t restrict filenames - users can use any name like temp.md
  • Handle files with special characters in content gracefully

Docusaurus Configuration

  • Don’t use navbar.logo without logo.src - it will throw validation error
  • Remove sidebarPath to let Docusaurus auto-generate sidebar from file structure
  • Use onBrokenLinks: 'ignore' to prevent build failures on broken links
  • Use markdown.hooks.onBrokenMarkdownLinks: 'ignore' (new location in v3.9+)
  • Don’t include deprecated options like root-level onBrokenMarkdownLinks
  • For local search, use @easyops-cn/docusaurus-search-local (no API key needed)
  • Algolia search requires API key - not suitable for generic themes

VitePress Configuration

  • Use ignoreDeadLinks: true to prevent build failures on broken/dead links
  • VitePress does NOT auto-generate sidebar - need custom script or manual config
  • sidebar: 'auto' does NOT work like Docusaurus - only shows current page headings
  • Output directory is .vitepress/dist (not build)
  • Use srcDir: 'docs' to specify source directory
  • Exclude node_modules in content backup
  • For local search, use search: { provider: 'local' } (built-in)
  • logo.link does NOT support external URLs - need custom theme to override
  • Write custom Node.js script to scan directory and generate sidebar config
  • Sort entries: files first, then directories (or vice versa for consistency)
  • Skip files starting with . or _
  • Skip index.md from sidebar items (it’s the folder landing page)
  • Capitalize first letter and replace - with spaces for display text

Homepage Selection Logic

Priority order:

  1. docs/index.md or content/index.md - explicit homepage
  2. docs/README.md or content/README.md - common convention
  3. First .md file (sorted alphabetically) - fallback
  • Always use sort before head -1 for consistent results
  • For Docusaurus: check if slug: / already exists before adding frontmatter
  • For VitePress: index.md is automatically the homepage, no frontmatter needed

GitHub Actions / Reusable Workflows

  • Reusable workflows MUST be in .github/workflows/ directory
  • Use on: workflow_call for workflows that should only be called by others
  • Caller workflow must declare permissions for the called workflow to use
  • Remove push and workflow_dispatch triggers if workflow should not run directly

Content Backup

  • Exclude README.md from root (usually not documentation)
  • Exclude hidden directories (.git, .github, etc.)
  • Exclude node_modules directory
  • Copy all user folders to preserve structure
  • Handle empty content directory - create default welcome page

Build & Deploy

  • GitHub Pages keeps old version online during build (atomic deployment)
  • First deployment shows blank page until complete - this is normal
  • Public repos have unlimited GitHub Actions minutes
  • Add npm cache to speed up builds: actions/cache@v4 with ~/.npm path

Shell Script Best Practices (GitHub Actions)

  • Don’t use sed -i '1s/^/...\n.../' - \n may not work on all systems
  • Don’t use heredoc (<< EOF) inside YAML run: blocks - causes syntax errors
  • Use printf '%s\n' 'line1' 'line2' for multi-line content
  • Use temp files + cat + mv to prepend content to files
  • Always quote variables: "$TARGET" not $TARGET

Logo & Branding

  • Use https://github.com/USERNAME.png to get user/org avatar
  • VitePress: need custom theme (enhanceApp) to make logo link to external URL
  • Docusaurus: logo.href + logo.target: '_blank' works for external links
  • Set favicon with head: [['link', { rel: 'icon', href: '...' }]] (VitePress)
  • Docusaurus favicon can be external URL directly

Testing Scenarios

Before releasing a theme, test with:

  • Empty repository (no markdown files)
  • Single markdown file
  • Nested folder structure
  • Files with ${} syntax in content
  • Files with JSX-like syntax <Component />
  • Files with curly braces {}
  • Files with Vue template syntax (VitePress specific)
  • Unicode/special characters in filenames
  • Very long filenames
  • Files starting with _ (usually ignored by default)
  • Files containing localhost URLs (dead link check)
  • Repository without index.md (homepage fallback logic)

Starlight (Astro) Configuration

  • Use npm create astro@latest -- --template starlight for correct project structure
  • Starlight v0.33.0+ changed social config from object to array format
    • Old: social: { github: 'url' }
    • New: social: [{ icon: 'github', label: 'GitHub', href: 'url' }]
  • Every .md file MUST have title in frontmatter - Starlight will fail without it
  • Sidebar slug must be lowercase - uppercase will cause “slug does not exist” error
  • autogenerate: { directory: '.' } doesn’t work well - use specific directory names
  • Content goes in src/content/docs/ directory
  • Output directory is dist
  • Use .mdx extension for index file if needed

Rspress Configuration

  • Public assets go in docs/public/ (not project root public/)
  • Sidebar items must be an array, not 'auto' string
  • Output directory is doc_build
  • Exclude public directory from sidebar generation
  • Config file is rspress.config.ts (TypeScript)

Common Errors & Solutions

ErrorCauseSolution
navbar.logo.src is requiredUsing logo without srcRemove logo or add src
Could not parse expression with acornMDX parsing ${} as JSXUse markdown.format: 'md'
workflow is not reusableMissing workflow_call triggerAdd on: workflow_call
requesting pages: write but only allowed noneCaller missing permissionsAdd permissions to caller
onBrokenMarkdownLinks deprecatedOld config locationMove to markdown.hooks
Found dead link (VitePress)Broken links in markdownUse ignoreDeadLinks: true
sidebar: 'auto' not workingVitePress doesn’t auto-generateWrite custom sidebar script
Page Not Found on homepageMissing slug: / frontmatterAdd frontmatter to first doc
here-document delimited by end-of-fileHeredoc in YAML run blockUse printf instead
social: Expected array, received objectStarlight v0.33.0+ API changeUse array format for social links
title: Required (Starlight)Missing frontmatter titleAuto-add title from filename
slug does not exist (Starlight)Uppercase slug in sidebarConvert slug to lowercase
Entry docs → 404 was not foundNo valid index pageEnsure index.md/mdx exists with frontmatter
item.items.map is not a functionRspress sidebar items: 'auto'Use array of items, not string
Failed to read favicon (Rspress)Wrong public directory pathPut assets in docs/public/
Module not found: Can't resolve './assets/...'Invalid image path in markdownSet remarkImageOptions: false (Fumadocs) or staticImage: false (Nextra)
Unrecognized keys: "theme", "themeConfig"Nextra v4 API changedUse new nextra() config format
Export doesn't exist in target moduleSimplified source.ts missing exportsDelete files that depend on removed exports (e.g., app/og, app/llms-full.txt)
fatal: No url found for submodule pathDownloaded repo folder treated as submoduleAdd folder to .gitignore or git rm -r --cached folder
shallow cloned, latest modified time not presentedGitHub Actions shallow cloneWarning only, can ignore or use fetch-depth: 0
InvalidTocFile: not a valid TOC File (DocFX)Empty or malformed toc.ymlDelete invalid toc.yml from subdirectories
Logo/favicon not showing (DocFX)External URL in _appLogoPath/_appFaviconPathUse custom template JS to inject external URLs
Homepage 404 (VuePress)Using index.md instead of README.mdRename index.md to README.md
sidebar: 'auto' not showing files (VuePress)VuePress auto only shows headingsGenerate sidebar config via Node.js script
Theme has ads/sponsors (VuePress)Using @vuepress/theme-vueUse default theme (built-in, no ads)
Prerendered 5 routes only (Docus)Nuxt can’t crawl to doc pagesAdd routes to nitro.prerender.routes
404 on /docs/CHECKLIST (Docus)Uppercase filename in routeConvert routes to lowercase
Blank page on root (Docus)index file without .htmlCreate index.html redirect after build
@nuxtjs/mcp-toolkit not compatible (Docus)MCP needs serverWarning only, can ignore
robots.txt with base URL error (Docus)robots module conflictSet robots: { robotsTxt: false }
No sidebar on homepage (Docus)content/index.md is landing pagePut docs in content/1.docs/ subdirectory

Fumadocs Configuration

  • Fumadocs is based on Next.js - requires output: 'export' for static build
  • Use basePath for GitHub Pages deployment (e.g., /${REPO_NAME})
  • Set images: { unoptimized: true } for static export
  • Content goes in content/docs/ directory
  • Output directory is out (Next.js static export default)
  • Every .mdx file should have frontmatter with title and description
  • Use create-fumadocs-app with --template +next+fuma-docs-mdx+static for static export
  • Fumadocs MDX is the official content source
  • Config file is next.config.mjs (ESM)
  • Requires fumadocs-mdx/next plugin wrapper
  • Set remarkImageOptions: false in source.config.ts to prevent build failures from invalid image paths
  • Delete app/llms-full.txt and app/og directories (they depend on exports not in simplified source.ts)
  • Clear template’s default content/docs/* before copying user content (removes Hello World page)

Nextra Configuration

  • Nextra v4 uses App Router - structure is src/app/ and src/content/
  • Requires output: 'export' and images: { unoptimized: true } for static build
  • Use basePath via environment variable for GitHub Pages
  • Content goes in src/content/ directory
  • Output directory is out
  • Need mdx-components.js at project root for MDX components
  • Use nextra-theme-docs for documentation theme
  • Config uses nextra() wrapper with contentDirBasePath option
  • Homepage redirect to /docs since content is under /docs route
  • Nextra uses MDX - user content with ${}, <>, {} may cause JSX parsing errors
  • Set staticImage: false to prevent build failures from invalid image paths

VuePress Configuration

  • VuePress 1.x is Vue 2 based - use vuepress@^1.9.10
  • ⚠️ DO NOT USE @vuepress/theme-vue - it contains sponsor/advertisement content
  • Use default theme @vuepress/theme-default (built-in, no ads)
  • Content goes in docs/ directory
  • Output directory is docs/.vuepress/dist
  • Config file is docs/.vuepress/config.js
  • Use base: '/${REPO_NAME}/' for GitHub Pages subdirectory
  • themeConfig.logo supports external URLs directly
  • themeConfig.sidebar: 'auto' only shows current page headings - need Node.js script for full sidebar
  • Homepage is docs/README.md (not index.md) - must rename index.md to README.md
  • Use themeConfig.repo for GitHub link in navbar
  • Custom styles go in docs/.vuepress/styles/index.styl (Stylus)
  • Use enhanceApp.js for client-side customizations (e.g., footer injection)
  • Favicon via head: [['link', { rel: 'icon', href: 'URL' }]] - supports external URLs
  • VuePress uses Vue template syntax - double curly braces in markdown will be parsed as Vue expressions
  • Use @vuepress/plugin-back-to-top for back-to-top button (built-in plugin)
  • VuePress 1.x does NOT have native dark mode toggle - custom implementation is complex
  • Green scrollbar: apply only to .sidebar selector, not whole page

DocFX Configuration

  • DocFX is a .NET-based documentation framework - uses dotnet tool install -g docfx
  • Content goes in docs/ directory
  • Output directory is _site
  • Config file is docfx.json
  • Uses toc.yml for navigation structure (root for navbar, docs/ for sidebar)
  • _appLogoPath and _appFaviconPath do NOT support external URLs - need custom template
  • Use custom template with JavaScript to inject external avatar as logo/favicon
  • Remove root toc.yml to hide navbar tabs (e.g., “Docs” tab) when only one section
  • Use redirect_url frontmatter in index.md to redirect homepage to docs
  • Set _disableContribution: true to hide “Edit this page” link
  • Set _appFooter to customize or remove footer
  • Sidebar toc.yml format: - name: Title + href: file.md
  • Delete invalid toc.yml files from user content subdirectories
  • Exclude framework source folders in backup (fumadocs/, nextra/, docute/, etc.)

Docute Configuration

  • Docute is a pure frontend framework - no build process needed
  • Only needs index.html + markdown files
  • Uses CDN for docute.js and docute.css (unpkg.com/docute@4)
  • URL routing: / => /README.md, /foo => /foo.md, /foo/ => /foo/README.md
  • Sidebar must be manually configured or auto-generated via script
  • Logo is a Vue template string - use <span> wrapper with flex layout for avatar + text
  • Use darkThemeToggler: true for dark mode toggle
  • No npm cache needed since no build process

mdBook Configuration

  • mdBook is a Rust-based documentation tool - install via pre-built binary or cargo install mdbook
  • Content goes in src/ directory
  • Output directory is book/
  • Config file is book.toml (TOML format)
  • Requires src/SUMMARY.md to define table of contents structure
  • Homepage is src/README.md (linked as [Introduction](README.md) in SUMMARY.md)
  • SUMMARY.md format: - [Title](path/to/file.md) for chapters
  • Use # headers in SUMMARY.md to create section separators
  • [output.html] section in book.toml for HTML output settings
  • site-url in book.toml for base path (GitHub Pages subdirectory)
  • Custom theme via theme/ directory - can override index.hbs, head.hbs, CSS files
  • Favicon and logo require custom theme modifications (no direct config option)
  • Built-in themes: light, rust, coal, navy, ayu
  • default-theme and preferred-dark-theme in [output.html] section
  • Supports MathJax via mathjax-support = true
  • Built-in search functionality (no additional config needed)

Eleventy Configuration

  • Eleventy (11ty) is a JavaScript-based static site generator
  • Install via npm install @11ty/eleventy
  • Config file is eleventy.config.js or .eleventy.js
  • Default input directory is . (project root)
  • Default output directory is _site
  • Supports multiple template languages: Liquid, Nunjucks, Markdown, HTML, JavaScript
  • Use dir.input and dir.output in config to customize directories
  • Use pathPrefix for GitHub Pages subdirectory deployment
  • Layouts go in _includes/ directory by default
  • Data files go in _data/ directory (JSON, JS, or YAML)
  • Use frontmatter layout to specify template layout
  • Collections are auto-generated from tags or can be custom defined
  • Use eleventyConfig.addPassthroughCopy() for static assets
  • Markdown files need frontmatter with layout to use templates
  • No built-in theme - requires custom CSS/layout

Sphinx Configuration

  • Sphinx is a Python-based documentation generator
  • Install via pip install sphinx furo myst-parser
  • Config file is conf.py (Python)
  • Default source directory is docs/ or project root
  • Default output directory is _build/html/
  • Use myst-parser extension to support Markdown (default is reStructuredText)
  • Furo is a modern, clean theme (BSD license)
  • Use toctree directive to define navigation structure
  • html_theme = 'furo' to use Furo theme
  • html_logo and html_favicon for branding
  • html_theme_options for theme customization
  • Custom CSS goes in _static/ directory
  • Build with sphinx-build -b html docs docs/_build/html
  • License is BSD (Sphinx) + MIT (Furo) - very permissive

Docus Configuration

  • Docus is a Nuxt 4-based documentation framework (MIT license)
  • Install via npm install docus nuxt better-sqlite3
  • Config file is nuxt.config.ts with extends: ['docus']
  • Content goes in content/ directory with number prefixes for ordering (e.g., 1.docs/, 2.guide/)
  • Output directory is .output/public/
  • Use nuxt generate for static build
  • Homepage is special - content/index.md renders as landing page without sidebar
  • Documentation pages need subdirectory - put docs in content/1.docs/ to get sidebar layout
  • Number prefixes in filenames control order: 1.index.md, 2.guide.md/index, /guide
  • Use .navigation.yml in directories to set section titles
  • Frontmatter title is required for each page
  • app.config.ts goes in app/ directory for theme configuration
  • header.title and header.logo for branding
  • socials.github for GitHub link in header
  • Custom components override defaults - put in app/components/app/
  • AppHeaderLogo.vue - customize logo + title display
  • AppFooterLeft.vue - customize footer left (attribution)
  • AppFooterRight.vue - customize footer right (social icons, theme toggle)
  • Set empty AppFooterRight.vue to remove duplicate theme toggle
  • Logo click should link to /docs not / (override AppHeaderLogo.vue with <NuxtLink to="/docs">)
  • Favicon via app.head.link in nuxt.config.ts
  • Prerendering issue: Nuxt only prerenders routes it can crawl from homepage
  • Must explicitly list routes in nitro.prerender.routes or generate dynamically
  • Convert filenames to lowercase for routes (uppercase causes 404)
  • Create index.html redirect after build for root → /docs
  • robots: { robotsTxt: false } to avoid base URL error
  • @nuxtjs/mcp-toolkit warning is normal - not compatible with static generate

General Lessons Learned

Framework Categories

  1. Build-required frameworks (need npm install + build):

    • Docusaurus, VitePress, Starlight, Rspress, Fumadocs, Nextra
    • Need npm cache for faster builds
    • Output to specific directories (build/, dist/, out/, doc_build/)
  2. Pure frontend frameworks (no build, CDN-based):

    • Docsify, Docute
    • Just HTML + markdown files
    • Faster deployment, simpler setup
  3. Next.js-based frameworks:

    • Fumadocs, Nextra
    • Require output: 'export' for static build
    • Require images: { unoptimized: true } for static export
    • Use basePath for GitHub Pages subdirectory

Common Pitfalls

  • Image handling: MDX/Next.js frameworks try to import images - disable with remarkImageOptions: false or staticImage: false
  • Template default content: Many templates include “Hello World” or demo content - clear before copying user content
  • CLI argument changes: Framework CLIs change frequently - check latest docs (e.g., create-fumadocs-app changed from --name --template --src to --template +next+fuma-docs-mdx+static)
  • API version changes: Check for breaking changes (e.g., Starlight social config changed from object to array)
  • Sidebar in sidebar: Some frameworks show index page in sidebar - use index: true frontmatter to hide it

Branding Consistency

For all frameworks, implement:

  • Logo: User avatar + username/reponame in top-left
  • Favicon: User’s GitHub avatar (https://github.com/USERNAME.png)
  • GitHub link: Icon or text link to repository (right side of navbar)
  • Title: username/reponame in browser tab
  • Footer: Powered by NekoTick · {Framework} with links to both projects

All workflows must include a footer with attribution: Powered by NekoTick · {Framework}

  • NekoTick link: https://github.com/NekoTick/NekoTick
  • Framework link: GitHub repository of the framework (not official website)
  • NekoTick comes first (deployment service provider)
  • Framework comes second (underlying tool)
  • Use · as separator
  • Links should open in new tab (target="_blank")
FrameworkGitHub Link
Docusaurushttps://github.com/facebook/docusaurus
VitePresshttps://github.com/vuejs/vitepress
Starlighthttps://github.com/withastro/starlight
Rspresshttps://github.com/web-infra-dev/rspress
Fumadocshttps://github.com/fuma-nama/fumadocs
Nextrahttps://github.com/shuding/nextra
Docutehttps://github.com/egoist/docute
DocFXhttps://github.com/dotnet/docfx
Docsifyhttps://github.com/docsifyjs/docsify
MkDocs Materialhttps://github.com/squidfunk/mkdocs-material
VuePresshttps://github.com/vuejs/vuepress
mdBookhttps://github.com/rust-lang/mdBook
Eleventyhttps://github.com/11ty/eleventy
Hugohttps://github.com/gohugoio/hugo
Sphinxhttps://github.com/sphinx-doc/sphinx
Docushttps://github.com/nuxt-content/docus

Logo Implementation Patterns

FrameworkLogo Config
Docusaurusnavbar.logo.src + navbar.title
VitePressthemeConfig.logo + themeConfig.siteTitle
Fumadocsnav.title as JSX with <img> + <span>
NextraNavbar logo prop as JSX
Docutelogo as Vue template string
DocFXCustom template JS to replace #logo element
VuePressthemeConfig.logo (external URL supported)
Starlightlogo.src + title
DocusCustom AppHeaderLogo.vue with <NuxtLink> + <UColorModeImage> + <span>
Hugoparams.BookLogo in hugo.yaml (external URL supported)
  • Auto-generate from directory structure when possible
  • Convert filenames to titles: my-file.mdMy File
  • Handle nested directories as collapsible groups
  • Skip README.md / index.md from sidebar items (they’re folder landing pages)
  • Use sort for consistent ordering

Testing Checklist (Updated)

  • Files with invalid image paths (should not break build)
  • Files with special markdown syntax (${}, <>, {})
  • Empty repository (should show welcome page)
  • Nested folder structure (should generate proper sidebar)
  • Repository without index.md (should use first doc as homepage)

Multi-Language Support

  • VitePress/Docusaurus i18n is static - requires manual translation files
  • No automatic translation built-in
  • Options: browser translation, or integrate translation API (costs money)
  • Structure: docs/en/, docs/zh/ etc. for each language

Adding New Framework

When adding a new documentation framework to Docship:

1. Create Workflow File

  • Create .github/workflows/{framework}.yml
  • Use on: workflow_call with base_path_suffix and deploy inputs
  • Follow existing workflow structure for consistency

Every framework MUST include a footer with: Powered by NekoTick · {Framework}

  • NekoTick link: https://github.com/NekoTick/NekoTick
  • Framework link: GitHub repository of the framework
  • Use · (middle dot) as separator
  • Links should open in new tab (target="_blank")

Footer implementation by framework type:

Framework TypeImplementation Method
DocusaurusthemeConfig.footer.copyright
VitePressthemeConfig.footer.message
DocsifyPlugin with hook.afterEach
Docutefooter config option
StarlightCustom Footer.astro component
RspressthemeConfig.footer.message
MkDocs Materialcopyright in mkdocs.yml
HonKitJS injection in HTML files
DocFX_appFooter in globalMetadata
FumadocsFooter element in app/layout.tsx
Nextrafooter prop in Layout component
VuePressenhanceApp.js DOM injection
mdBookCustom theme/index.hbs or theme/head.hbs
EleventyCustom layout template with footer
SphinxCustom CSS in _static/custom.css
Docusfooter.credits in app.config.ts
HugoCustom partial layouts/partials/docs/inject/footer.html
Jekyllfooter_content in _config.yml

3. Update Showcase

  • Add build job in showcase.yml with name: {framework}
  • Add to needs array in combine job
  • Add build result env variable (e.g., BUILD_FRAMEWORK)
  • Add to RESULTS array in combine step
  • Add entry in showcase/frameworks.json with GitHub link(s)

frameworks.json format:

{
  "framework-name": {
    "links": ["https://github.com/org/repo"]
  },
  "framework-with-theme": {
    "links": ["https://github.com/org/framework", "https://github.com/org/theme"]
  }
}

4. Update Documentation

  • Add framework to README.md supported frameworks table
  • Add framework-specific notes to CHECKLIST.md if needed