Skip to content
sdocs

Doc Pages

A [DOC] block is freeform prose — install guides, design tokens, principles — anything that isn't documenting a single component. Its body is markdown, rendered as a page with an auto-generated table of contents.

Docs are documentation, so the prose renders with the docs app's own typography — the project's css never touches it. To showcase real components or tokens in the project's context, drop an [EXAMPLE] block anywhere in the flow: it renders in place on an isolated stage that loads the configured css, exactly like an example in a [SHOWCASE] entity.

<script lang="ts">
	import Button from './Button.svelte';
	const version = '2.0';
</script>

[DOC title="Docs / Getting Started"]

	## Installation

	Install sdocs (currently {version}) and create your first doc file:

	```bash
	npm install -D sdocs
	```

	## Buttons in context

	Components render on a stage, in your project's own css:

	[EXAMPLE title="Buttons" direction="row" gap="8px"]
		<Button label="Save" />
		<Button label="Delete" intent="danger" />
	[/EXAMPLE]

	| Command | Does |
	|---|---|
	| `sdocs dev` | dev server |
	| `sdocs build` | static site |

[/DOC]

Attributes

AttributeRequiredMeaning
titleyesSidebar path, /-separated like every entity
maxWidthnoWidth of the content column, toc included (default from config, 1200px)
paddingnoSpace around the page content (default 32px)
contentXnoAligns the content column: left/center/right (default left)
tocnotoc="false" hides the table of contents (default true)
slugnoOverrides the URL segment (default: slugified title segment)
hidenoA bare flag: routable, but never listed in a sidebar

The maxWidth box holds the prose and the table of contents; when the toc is hidden (or the page has no headings), the prose takes its space. A body that opens with a # heading uses it as the page's displayed title — the title attribute then only names the sidebar entry.

Home page

The landing page is picked in the config, by route path:

// sdocs.config.js
home: 'guides/introduction'

That entity renders at the root route and is what the logo/title links to — any entity kind works. It stays listed in its section's sidebar; add the hide flag to its opener to make the logo its only link. A home path that resolves nowhere is an error. When no home is set, the root shows the built-in About page instead.

About page

Every site has an About page at /about: the project logo, the counts of components, pages, and layouts, and the sdocs version that built the site. It's the default landing page whenever the config sets no home, and it stays reachable at /about either way — including from the button in the top bar.

Examples in pages

An [EXAMPLE] inside a page works like an example in a component doc: a frozen Svelte snippet rendered live, with a collapsed code panel under it. It is the only part of a page where the configured css loads, which makes the boundary easy to reason about: prose is docs-styled, stages are project-styled.

AttributeRequiredMeaning
titleyesThe example heading
descriptionnoShort text under the heading — inline markdown renders styled
tagsnoWhat this example shows, comma-separated — shown as chips and searchable through the MCP server
codenocode="false" hides the source panel, leaving only the rendered stage
maxWidth / paddingnoStage size (defaults from content.showcase)
direction / gapnoStage flex flow (defaults from content.showcase)
contentX / contentYnoStage alignment (defaults from content.showcase)
backgroundnoStage background — a CSS color or a var() from the project's css
minHeightnoMinimum stage height, so a short example still has room

Each example is self-contained: it sees the file <script> (imports and shared values), but not snippets declared in the prose — the two compile into different worlds.

The markdown dialect

The body is markdown first, with two Svelte conveniences:

  • {expression} interpolation — values from the file's <script> drop into the prose: currently {version}. The inside of a balanced {…} is passed to Svelte verbatim, so string literals and operators are fine: {format("0.0.1")}.
  • Svelte islands — markup blocks that pass to Svelte untouched. A line that starts with a component or HTML tag (<Button, <div) or a Svelte block ({#snippet, {@render), sitting after a blank line, begins an island; the island runs until its tags and blocks are balanced — blank lines inside are fine. Markdown never reformats or splits an island.

Islands run in the docs context — they're for structuring the page itself (custom layouts around prose, repeated markup via snippets). Anything that should look like your product belongs in an [EXAMPLE].

Everything else is GitHub-flavored markdown: headings, links, images, tables (with :---: alignment), lists, task lists (- [x]), ~~strikethrough~~, blockquotes, horizontal rules, and code fences. Fences are inert — code inside them is displayed and highlighted, never executed, even if it looks like a component tag (an [EXAMPLE] opener inside a fence is content too). The same goes for `inline code`. For a literal brace in prose, escape it: \{.

Some details worth knowing:

  • Fence languages — anything shiki knows (svelte, ts, bash, …) plus sdoc itself, so docs about .sdoc files highlight natively.
  • Alerts — a blockquote whose first line is [!NOTE], [!TIP], [!IMPORTANT], [!WARNING], or [!CAUTION] renders as a tinted callout, GitHub-style.
  • Links — external (http…) links open in a new tab; internal and relative links stay in the app.
  • Images![alt](/hero.png) works with a static folder configured; assets are served at the site root in dev and copied into the build.

Snippets

Pages support snippets for repeated markup. Declare one anywhere in the body as its own island, then render it anywhere — before or after, in any section:

[DOC title="Colors"]

	{#snippet swatch(color: string)}
		<div style="background: {color}; width: 100px; height: 100px;"></div>
	{/snippet}

	## Reds

	<div style="display: flex;">
		{@render swatch('#ff0000')}
		{@render swatch('#b91c1c')}
	</div>

	## Blues

	<div style="display: flex;">
		{@render swatch('#3b82f6')}
	</div>

[/DOC]

What a page does not have: controls and prop extraction. If you find yourself wanting stateful logic, the content is probably a component doc or a layout.

Table of contents

A table of contents is generated from the page's ###### headings and shown on the right; as the page scrolls, the entry for the section in view highlights. Heading IDs are slugified — lowercased, non-word characters stripped, spaces as hyphens — so ## Getting Started anchors at #getting-started. The page header shows the entity's name — the title's last path segment (groups and folders already structure the sidebar); a top-level # heading in the body is neither required nor listed.

When to reach for a doc

  • Doc — content that doesn't map to one component: guides, overviews, conventions.
  • Component doc — one component, with controls and extracted API.
  • Svelte page — a designed page built in plain Svelte: landing pages, custom routes.
  • Layout — components composed together on a full-page stage.