Contribute

Documentation

Where a docs page belongs, how to write it so readers know what it is for, and how to check its links.

This page is for anyone who adds or edits a page on this documentation site. It tells you where a page goes, which template it follows and how to write it. It ends with the link check every pull request runs.

The site is @frigear-nu/docs, a Docus app under apps/docs. It documents the whole repository for developers who build on the Frigear modules. Product documentation for end users belongs in apps/website.

Run it from the repository root:

pnpm install
pnpm --filter @frigear-nu/docs dev
pnpm --filter @frigear-nu/docs build

Where a page belongs

Content lives in apps/docs/content/, in five numbered folders that are the header tabs:

FolderTabPut here
1.get-startedGet StartedThe step-by-step path from zero to a running, deployed app.
2.guideGuideConcepts, UX patterns, best practices and going further.
3.modules/<category>ModulesOne reference page per module, in its category folder: 1.identity, 2.commerce, 3.features, 4.interface or 5.platform.
4.examplesExamplesStarters, reference apps and short task examples.
5.contributeContributeWorking on this repository: development, writing a module, publishing, starters, docs and tooling.

Add or update a page

  1. Pick the folder from the table above. A new section gets a numbered folder with a .navigation.yml for its title and icon.
  2. Add a numbered Markdown file, for example 3.modules/3.features/5.my-module.md. Docus sorts navigation by file name as a string, so use unique two-digit prefixes; the name after the number becomes the route (/modules/features/my-module). Inserting a page in the middle may mean renumbering its siblings, which keeps their routes.
  3. Add frontmatter with a title, a one-sentence description and, optionally, a navigation icon:
    ---
    title: My module
    description: One sentence on what the page covers.
    navigation:
      icon: i-lucide-puzzle
    ---
    
  4. Write focused Markdown about the repository, using the page templates below. Check code and package READMEs before documenting behavior.
  5. Run the docs app and check the page and its navigation.
  6. Run pnpm docs:check and fix what it reports.
Moving or renaming a page changes its route and breaks every link to the old one. Do it only when you also:
  1. Add an old to new entry in the redirects of apps/docs/nuxt.config.ts.
  2. Update every link to the old route.
  3. Run pnpm docs:check and make it pass.

Conventions

  • Titles. Use the plain name (Auth, Cookie consent), never a Module suffix. Headings are sentence case.
  • Page layout. Use the page templates below.
  • Code and reference. Signatures, types and payloads go in fenced ts blocks. Hooks, routes, options and environment variables go in tables, not running prose.
  • Accuracy. Check defaults, routes and hooks against the source before documenting them.
  • Nuxt 4. Write every example as Nuxt 4 code (app/ directory, defineNuxtConfig), never Nuxt 3 or 2 patterns.
  • Links. Link to pages by route (/modules/identity/auth), never by numbered file name.
  • Starters. A page that describes a starter stays in step with the files in .starters/.

Writing

Readers told us the docs left them asking "what is this even for?". These rules answer that question on every page. Each row shows a sentence from the current docs and its rewrite.

RuleBeforeAfter
The first screen says what it is, who it is for and what you get. Code names come after."The Nuxt module that owns authentication: …""Lets people sign in to your app with a password, an emailed link, a passkey or GitHub/Google, and tells every server route who is calling."
One fact per sentence."Only the SHA-256 is stored, and they are accepted in the Authorization header only.""Only a hash of the token is stored. Send it in the Authorization header."
Inline code at most once per sentence, and never before its plain explanation."A caller is one of three kinds, all resolved into the same AuthPrincipal""Auth works out who made the request and how they proved it. It hands your route that answer, the principal (AuthPrincipal)."
Expand acronyms on first use per page."PKCE sign-in for SPAs and Capacitor apps""sign-in with PKCE (Proof Key for Code Exchange) for single-page apps (SPAs) and Capacitor apps"
Link the glossary term on first use per page.The first "staff" on a page has no link."staff" on first use, plain afterwards.
Never two names for one concept."OAuth client" and "application" on the same page."OAuth application" throughout.
Number procedures in execution order, with prerequisites before step 1."Then: 1. … 3. Implement the hooks. Without them nobody is an admin."A ::warning "Without these hooks no one can manage applications" before step 3.
Put a ::warning before the step it protects, never after.The migration warning follows the config block.The migration is its own step, with the warning first.
Use ::caution for irreversible or security-relevant steps.Key rotation written as plain prose.::caution "Use a different signing key per environment."
A table cell holds one fact in under about 25 words.A 45-word purpose cell for afterLogin.The cell "Where users land after signing in.", plus an afterLogin paragraph below the table.
No wall of prose.Three unbroken paragraphs after an options table.At most about six paragraphs between a table, callout or code block.

Most pages carry several callouts. Pick each one by its job:

CalloutUse it for
::noteBackground the reader needs for the next step.
::tipA recommendation or the shortest path.
::warningWhat breaks if a step is skipped. It goes before that step.
::cautionA step that is irreversible or affects security.
::callout{color="success" icon="i-lucide-check"}What you should see after a procedure.

Options. The lead sentence of an options section is always "Options go under the x key." The table then shows each option's full path from that key, for example settings.pages.admin. Long explanations go below the table as paragraphs that start with the option name in bold. A module without options says "The module has no options."

Page templates

Most pages follow one of five templates. Pick the tab for the page you are writing.

Other pages, such as best practices, UX patterns, going further and contribute pages, have no fixed template. They still open by saying what the page is for and who it is for, and they end with ## Next steps.

For the index.md of a module folder, or a module that has a single file. The front matter description says what the module is for in one sentence, not a feature list.

  1. No heading. Two to four plain sentences: what the module does for the host app and its users, when to use it, and when to use something else (one link). The package name does not appear before the second sentence.
  2. ## What you get. Bullets in reader terms, grouped as Pages (paths), Admin area (screens), Server routes, Composables and helpers, and Emails. Mark each item as on by default or opt-in.
  3. :module-runtime{…}. The badge row, unchanged.
  4. ::note with requirements. Exactly three lines: Installed for you:, You add: and Optional:. Put nothing else in this note. When requirements differ by mode, a line may name it, as in You add (server mode):.
  5. ## Quick setup. A ::steps block: install, then nuxt.config.ts, then a migration if the module adds tables, then the role grants if it has staff permissions. A ::warning comes before the migration step and before the grants step ("without this nobody can …"). A config-key gotcha such as "authentication, not auth" gets its own ::warning here. End with a What you should see success callout.
  6. ## Options. The lead sentence "Options go under the x key.", then a table of option (full path), type, default and purpose. Long explanations follow as paragraphs led by the option name in bold. Runtime config and environment variables go under ### Runtime config.
  7. Module with a single file: ## Server, ## App, ## Hooks, ## Routes, ## Database and ## Translations, in that order. Each opens with one sentence, as on a sub-page. Module with a folder: ## Module pages, a table of page, what it covers and who it is for or which auth mode. It sits after Options. Module with a single file and one sub-page: Options, the single-file sections, ## Module pages, then ## Next steps.
  8. ## Next steps. See the closing rule below.

Closing section. Every page ends with ## Next steps, never ## Next, ## Related or ## On this module. The landing page (index.md) is the exception: it ends with a "Next steps" page section instead of a heading. It holds at most four links. Each is a bullet in the form [Page](/route) – why you would go there now, in one sentence. Put the most likely next action first, and do not repeat links from the opening paragraph, except the alternative module that a module overview intro must link. The link text is the page title; the navigation title carries the page's role.

Some explanations used to appear on several pages, and the copies drifted apart. Each one now has a keeper page with the full text. Other pages give one sentence and link to the keeper.

ConceptPages that repeat it todayKeeper page
How modules depend on each otherArchitectureModules. Architecture keeps only the rule that dependencies point one way.
The kinds of credentialAuth, Auth server API, Protect a routeThe principal
The sessionOnly ruleAuthentication, Users, Auth server API, Protect a route, Send an email, HTTP routessessionOnly
Nuxt 4 onlyModules, Architecture, Base layer, OverviewIntroduction, in one ::note. This page keeps the writing rule.
Adding a moduleWorkspace starterAdd a module
RequirementsMobile starter, Standalone mobile starter, Local developmentRequirements. The others list extra tools only.
Public and confidential applicationsOAuth Server helpers, Client mode, Standalone mobile starter, Use your own OAuth server, OAuth applicationThe concept: Identity. The mechanics: Public applications.
Migrations and dialectsConfiguration, Database tablesFor app developers: Database. For module authors: Module Kit database helpers. For extending tables: Database extensions.

Pictures

Use screenshots for layouts, such as those on the UX pattern pages. Use text diagrams in a text code block for flows, such as a sign-in across apps. Images live in apps/docs/public/images/<section>/<page>-<pattern>.png, and a pull request that changes a layout in packages/ui or packages/admin recaptures them.

Vocabulary

The glossary lists the one word these docs use for each concept, and the words to avoid. Link a glossary term to it the first time a page uses that term, then write it plainly.

pnpm docs:check runs scripts/docs-check-links.mjs. It is plain Node with no dependencies, so it runs in seconds. The Docs workflow (.github/workflows/docs.yml) runs it on every pull request that touches the docs.

It checks:

CheckWhat fails
RoutesA link to a page that does not exist, or an /images/… path missing from apps/docs/public.
AnchorsA #anchor that matches no heading on the target page.
RedirectsA redirect target with no page. A link to a redirect source is only a warning: use the new route.
External linksA docs.frigear.dev URL in package homepages, package READMEs, starter AGENTS.md files, the root README.md or AGENTS.md that no longer resolves.
GlossaryA route in the glossary's "Where it is explained" column that does not resolve.

Anchors follow the rule of @nuxtjs/mdc, not plain GitHub slugs. The heading text is lowercased and stripped of punctuation, and spaces become dashes. Repeated dashes then collapse to one, and a leading digit gets a _ prefix. So ## 1. Require a signed-in user becomes #_1-require-a-signed-in-user.

Add --terms to list the words the glossary says to avoid, counted per file. For example, pnpm docs:check --terms apps/docs/content/1.get-started scans one folder. It only reports. Add --strict to make each hit an error once a folder is clean.

Errors make the run exit with code 1. Warnings do not.

Next steps

Copyright © 2026