---
# Install Oxidoc - Linux, macOS, and Windows (docs/installation)
---
title: Install Oxidoc - Linux, macOS, and Windows
description: Install Oxidoc with a single command on Linux, macOS, or Windows. Learn how to update, pin versions, and configure your install directory.
---
# Installation
## Install Script
The recommended way to install Oxidoc is the install script, which detects your platform and downloads the correct binary.
```bash
curl -fsSL https://oxidoc.dev/install.sh | sh
```
```powershell
irm https://oxidoc.dev/install.ps1 | iex
```
The script detects your platform, downloads the correct binary, and adds it to your `PATH`.
### Custom Install Directory
Set `OXIDOC_INSTALL_DIR` to change the install location:
```bash
OXIDOC_INSTALL_DIR=/usr/local/bin \
curl -fsSL https://oxidoc.dev/install.sh | sh
```
## GitHub Releases
Download a pre-built binary directly from [GitHub Releases](https://github.com/oxidoc-lab/oxidoc/releases).
| Platform | Architecture | Binary |
|:---------|:-------------|:-------|
| Linux | x86_64 | `oxidoc-x86_64-unknown-linux-gnu` |
| Linux | aarch64 | `oxidoc-aarch64-unknown-linux-gnu` |
| macOS | x86_64 | `oxidoc-x86_64-apple-darwin` |
| macOS | Apple Silicon | `oxidoc-aarch64-apple-darwin` |
| Windows | x86_64 | `oxidoc-x86_64-pc-windows-msvc.exe` |
## Verify Installation
```bash
oxidoc --version
```
## Update
Update to the latest stable release:
```bash
oxidoc update
```
Install the latest prerelease:
```bash
oxidoc update --pre
```
## Switch Versions
Pin a specific version:
```bash
oxidoc set-version v0.1.0
```
This downloads and installs that exact version, useful for locking CI to a known-good release.
## Uninstall
Remove the binary and its directory:
```bash
rm -rf ~/.oxidoc
```
Remove the `PATH` entry from your shell config (`~/.bashrc`, `~/.zshrc`, etc.) if it was added.
---
# Quickstart - Build Your First Docs Site in 5 Minutes (docs/quickstart)
---
title: Quickstart - Build Your First Docs Site in 5 Minutes
description: Go from zero to a running documentation site in under five minutes. Install Oxidoc, create a project, start the dev server, and build for production.
---
# Quickstart
Go from zero to a running documentation site in under five minutes.
## 1. Install Oxidoc
```bash
curl -fsSL https://oxidoc.dev/install.sh | sh
```
See [Installation](/docs/installation) for other options.
## 2. Create a Project
```bash
oxidoc init my-docs
cd my-docs
```
This creates a directory with a starter `oxidoc.toml`, a `home.rdx` landing page, and a `docs/` directory with sample content.
## 3. Start the Dev Server
```bash
oxidoc dev
```
Open [http://localhost:3000](http://localhost:3000) in your browser. You'll see the starter site with a landing page and sample documentation.
The dev server watches for changes and rebuilds automatically:
- `.rdx` files in all content directories
- `oxidoc.toml` configuration
- `assets/` directory (custom CSS, images, JS)
- Root-level `.rdx` files (`home.rdx`, etc.)
## 4. Edit Content
Open `docs/quickstart.rdx` in your editor and make a change. Save the file — the browser refreshes automatically.
RDX files are Markdown with embedded components. For example:
---
title: Introduction
---
# Introduction
Welcome to my docs. Here's a tip:
Edit any `.rdx` file and the browser refreshes automatically.
See [Writing Content](/docs/writing-content) for the full guide on RDX syntax.
## 5. Add a New Page
Create a new file `docs/guide.rdx`:
---
title: Guide
---
# Guide
Your content here.
Then add it to the navigation in `oxidoc.toml`:
navigation = [
{ path = "/docs", dir = "docs", groups = [
{ group = "Getting Started", pages = ["quickstart", "guide"] },
] },
]
Save the file and the dev server rebuilds automatically — the new page appears in the sidebar.
## 6. Build for Production
```bash
oxidoc build
```
The output is a fully static site in `dist/`. Deploy it to any static host — see [Deployment](/docs/deployment) for guides on GitHub Pages, Vercel, and Netlify.
## Next Steps
Learn RDX syntax, frontmatter, and file organization.
Explore all built-in components with examples.
Full reference for oxidoc.toml.
---
# Writing Content - RDX Syntax and File Organization (docs/writing-content)
---
title: Writing Content - RDX Syntax and File Organization
description: Learn RDX, Oxidoc's content format. Write Markdown with embedded components, configure frontmatter, organize files, and create Mermaid diagrams.
---
# Writing Content
Oxidoc uses **RDX** — Markdown with embedded components. Files use the `.rdx` extension.
## File Organization
Content lives in directories mapped to site sections via `oxidoc.toml`:
```text
my-docs/
├── oxidoc.toml
├── home.rdx
└── docs/
├── intro.rdx
├── guide.rdx
└── advanced/
└── plugins.rdx
```
Nested folders create nested URL paths. `docs/advanced/plugins.rdx` is served at `/docs/advanced/plugins`.
### Ordering with Numbered Prefixes
Prefix files with numbers to control sidebar order without changing slugs:
```text
docs/
├── 01-intro.rdx → /docs/intro
├── 02-quickstart.rdx → /docs/quickstart
└── 03-advanced.rdx → /docs/advanced
```
The numeric prefix is stripped from the URL slug.
## Frontmatter
Every `.rdx` file starts with YAML frontmatter between `---` fences:
```yaml
---
title: Page Title — Long Descriptive Title for SEO
short_title: Page Title
description: Optional meta description for SEO
layout: landing
---
```
| Field | Required | Description |
|:------|:---------|:------------|
| `title` | Yes | Full page title — shown in the browser tab and top bar |
| `short_title` | No | Short title shown in the sidebar navigation. Falls back to the filename if omitted |
| `description` | No | Meta description for SEO and search snippets |
| `layout` | No | Set to `landing` for full-width landing pages (no sidebar/TOC) |
## Markdown Features
RDX supports standard Markdown:
- **Headings** — `#` through `######`, auto-linked with anchors
- **Bold** / *Italic* / ~~Strikethrough~~
- Ordered and unordered lists
- [Links](https://example.com) and images ``
- Block quotes with `>`
- Horizontal rules with `---`
### Code Blocks
Fenced code blocks with syntax highlighting:
````markdown
```rust
fn main() {
println!("Hello!");
}
```
````
### Tables
Standard Markdown tables with alignment:
```markdown
| Left | Center | Right |
|:-----|:------:|------:|
| a | b | c |
```
### Mermaid Diagrams
Use `mermaid` as the language for a fenced code block:
````markdown
```mermaid
graph LR
A[Write] --> B[Build] --> C[Deploy]
```
````
The diagram is rendered automatically — no configuration needed.
## Embedding Components
Use HTML-like tags to embed interactive components in your Markdown:
````markdown
Here is a callout:
This renders as an interactive callout component.
And tabs:
Content oneContent two
````
Components can contain Markdown. See [Components](/docs/components) for the full list.
## File-System Routing vs Explicit Navigation
By default, pages appear in the sidebar only if listed in `oxidoc.toml` navigation groups:
groups = [
{ group = "Getting Started", pages = ["intro", "quickstart"] },
]
Pages not listed in navigation are still built and accessible by URL — they just don't appear in the sidebar. This is useful for pages linked inline but not important enough for the sidebar.
## Root Pages
Pages outside of any section (like a landing page) are configured under `[routing.root]`:
[routing.root]
homepage = "home.rdx"
pages = ["about.rdx", "contact.rdx"]
The `homepage` is rendered at `/`. Additional `pages` are rendered at `/{name}`.
---
# Configuration Reference — oxidoc.toml (docs/configuration)
---
title: Configuration Reference — oxidoc.toml
description: Complete reference for oxidoc.toml. Configure your project, theme, navigation, search, versioning, i18n, analytics, and more with every field documented.
---
# Configuration
All configuration lives in `oxidoc.toml` at the project root.
## `[project]`
Core project metadata.
[project]
name = "My Docs"
description = "Documentation for my project"
logo = "/assets/logo.svg"
favicon = "/assets/favicon.svg"
base_url = "https://example.com"
edit_url = "https://github.com/org/repo/blob/main"
edit_label = "Edit this page"
debug_islands = false
| Field | Type | Default | Description |
|:------|:-----|:--------|:------------|
| `name` | String | **required** | Project name shown in header and metadata |
| `description` | String | — | Site description for SEO and feeds |
| `logo` | String | — | Path to logo image (relative to assets) |
| `favicon` | String | — | Path to favicon (`.ico`, `.svg`, or `.png`) |
| `base_url` | String | — | Full base URL for sitemap, feeds, and canonical links |
| `edit_url` | String | — | Base URL for "edit this page" links (e.g. GitHub blob URL) |
| `edit_label` | String | `"View page source"` | Label for the edit/source link on each page |
| `debug_islands` | bool | `false` | Show debug outlines on Wasm island components |
## `[theme]`
Visual customization.
[theme]
primary = "#3b82f6"
accent = "#f59e0b"
font = "Inter, sans-serif"
code_font = "JetBrains Mono, monospace"
dark_mode = "system"
custom_css = ["assets/custom.css"]
| Field | Type | Default | Description |
|:------|:-----|:--------|:------------|
| `primary` | String | `"#2563eb"` | Primary color (links, buttons, active states) |
| `accent` | String | `"#f59e0b"` | Accent color for decorative elements |
| `font` | String | System font stack | Body text font family |
| `code_font` | String | Monospace stack | Code font family |
| `dark_mode` | String | `"system"` | Dark mode: `"system"`, `"light"`, or `"dark"` |
| `custom_css` | String[] | `[]` | Custom CSS files to load (paths relative to project root) |
See [Theming](/docs/theming) for the full CSS variable reference.
## `[routing]`
Controls site structure, navigation, and sections.
### `[routing.root]`
[routing.root]
homepage = "home.rdx"
pages = ["about.rdx", "contact.rdx"]
| Field | Type | Default | Description |
|:------|:-----|:--------|:------------|
| `homepage` | String | — | RDX file rendered at `/` |
| `pages` | String[] | `[]` | Additional root-level pages rendered at `/{name}` |
### `header_links`
[routing]
header_links = [
{ label = "Docs", href = "/docs" },
{ label = "GitHub", href = "https://github.com/org/repo" },
]
| Field | Type | Description |
|:------|:-----|:------------|
| `label` | String | Link text displayed in the header |
| `href` | String | URL (internal path or external URL) |
### `navigation`
Array of site sections. Each section has its own sidebar and content directory.
[routing]
navigation = [
{ path = "/docs", dir = "docs", groups = [
{ group = "Getting Started", pages = ["intro", "quickstart"] },
{ group = "Guides", pages = ["guides/styling"] },
] },
{ path = "/api", openapi = "./openapi.yaml" },
]
| Field | Type | Description |
|:------|:-----|:------------|
| `path` | String | Base URL path (e.g., `"/docs"`, `"/api"`) |
| `dir` | String | Content directory for `.rdx` files (relative to project root) |
| `groups` | Array | Sidebar groups, each with `group` (title) and `pages` (slug list) |
| `openapi` | String | Path to OpenAPI spec — auto-generates API reference pages |
Sections are fully independent — each has its own sidebar. You can mix `.rdx` content sections with OpenAPI sections:
[routing]
navigation = [
{ path = "/docs", dir = "docs", groups = [
{ group = "Guides", pages = ["intro", "quickstart"] },
{ group = "Advanced", pages = ["plugins", "theming"] },
] },
{ path = "/api", openapi = "./openapi.yaml" },
{ path = "/internal", dir = "internal-docs", groups = [
{ group = "Team", pages = ["onboarding", "runbooks"] },
] },
]
Each section's `dir` is resolved relative to the project root. You can have as many content directories as you need — they don't have to be called `docs`.
Pages can use subdirectories — `"guides/styling"` maps to `docs/guides/styling.rdx`.
## `[search]`
[search]
provider = "oxidoc"
semantic = true
model_path = "./models/embedding.gguf"
| Field | Type | Default | Description |
|:------|:-----|:--------|:------------|
| `provider` | String | `"oxidoc"` | Search provider: `"oxidoc"`, `"algolia"`, `"typesense"`, `"meilisearch"`, `"custom"` |
| `semantic` | bool | `false` | Enable semantic (hybrid) search alongside BM25 |
| `model_path` | String | — | Path to custom GGUF embedding model |
Provider-specific fields — see [Search](/docs/search) for details:
| Provider | Fields |
|:---------|:-------|
| Algolia | `app_id`, `api_key`, `index_name` |
| Typesense | `host`, `port`, `protocol`, `api_key`, `collection_name` |
| Meilisearch | `host`, `api_key`, `index_name` |
| Custom | `stylesheet`, `script`, `init_script` |
## `[versioning]`
[versioning]
default = "v2.0"
versions = ["v1.0", "v2.0"]
| Field | Type | Default | Description |
|:------|:-----|:--------|:------------|
| `default` | String | — | Default version served at the root URL |
| `versions` | String[] | `[]` | All available versions |
See [Versioning](/docs/versioning) for the full guide.
## `[i18n]`
[i18n]
default_locale = "en"
locales = ["en", "ja", "es"]
translation_dir = "i18n"
| Field | Type | Default | Description |
|:------|:-----|:--------|:------------|
| `default_locale` | String | `"en"` | Default locale (served at root, no prefix) |
| `locales` | String[] | `[]` | All configured locales |
| `translation_dir` | String | `"i18n"` | Directory for Fluent `.ftl` translation files |
See [i18n](/docs/i18n) for the full guide.
## `[footer]`
[footer]
copyright_owner = "My Company"
copyright_owner_url = "https://example.com"
links = [
{ label = "Privacy", href = "/privacy" },
{ label = "Terms", href = "/terms" },
]
| Field | Type | Default | Description |
|:------|:-----|:--------|:------------|
| `copyright_owner` | String | — | Name shown in copyright notice |
| `copyright_owner_url` | String | — | URL for the copyright owner name |
| `links` | Array | `[]` | Footer links, each with `label` and `href` |
## `[social]`
[social]
github = "https://github.com/org/repo"
discord = "https://discord.gg/invite"
Social links displayed in the header/footer.
## `[analytics]`
[analytics]
google_analytics = "G-XXXXXXXXXX"
# Or inject a custom script:
# script = ''
| Field | Type | Default | Description |
|:------|:-----|:--------|:------------|
| `google_analytics` | String | — | Google Analytics measurement ID |
| `script` | String | — | Custom analytics script tag (injected in `
`) |
See [Analytics](/docs/analytics) for details.
## `[redirects]`
[redirects]
redirects = [
{ from = "/old-page", to = "/new-page" },
{ from = "/guide", to = "/docs/quickstart" },
]
Each entry generates an HTML file at `from` that redirects to `to`.
## `[components.custom]`
Register Vanilla JS Web Components:
[components.custom]
PromoBanner = "assets/js/promo-banner.js"
FeedbackWidget = "assets/js/feedback-widget.js"
Maps component tag names to JavaScript file paths. See [Custom Components](/docs/guides/custom-components).
## `[attribution]`
[attribution]
oxidoc = true
| Field | Type | Default | Description |
|:------|:-----|:--------|:------------|
| `oxidoc` | bool | `true` | Show "Built with Oxidoc" in the footer |
---
# CLI Reference — All Commands and Flags (docs/cli)
---
title: CLI Reference — All Commands and Flags
description: Complete CLI reference for Oxidoc. Learn every command — init, dev, build, clean, archive, update — with flags, options, and usage examples.
---
# CLI Reference
## Global Flags
These flags work with any command:
| Flag | Description |
|:-----|:------------|
| `-C, --project ` | Project root directory (defaults to current directory) |
| `-v, --verbose` | Enable verbose output with detailed build steps |
| `-q, --quiet` | Suppress all output except errors (useful for CI) |
## Commands
### `oxidoc init`
Initialize a new Oxidoc project.
```bash
oxidoc init my-docs
```
Creates a directory with a starter `oxidoc.toml`, `home.rdx` landing page, and sample docs.
| Flag | Description |
|:-----|:------------|
| `` | Project directory name (optional, defaults to current directory) |
| `-f, --force` | Force overwrite existing files |
| `-y, --yes` | Skip confirmation prompts |
### `oxidoc dev`
Start the development server with hot reload.
```bash
oxidoc dev
oxidoc dev -p 8080
```
| Flag | Description |
|:-----|:------------|
| `-p, --port ` | Port to serve on (default: `3000`) |
Edit any `.rdx` file and the browser refreshes automatically.
### `oxidoc build`
Build the site for production (static site generation).
```bash
oxidoc build
oxidoc build -o public
```
| Flag | Description |
|:-----|:------------|
| `-o, --output ` | Output directory (default: `dist`) |
Generates a fully static site with all HTML, CSS, JS, Wasm, and search index files.
### `oxidoc clean`
Remove build artifacts (`.oxidoc-dev/` and `dist/` directories).
```bash
oxidoc clean
```
### `oxidoc archive`
Manage versioned documentation archives.
```bash
# Create an archive from current docs
oxidoc archive create v1.0
# List all archives
oxidoc archive list
# Delete an archive
oxidoc archive delete v1.0
```
| Subcommand | Description |
|:-----------|:------------|
| `create ` | Snapshot the current docs as a version archive |
| `list` | List all available archived versions |
| `delete ` | Delete a specific version archive |
See [Versioning](/docs/versioning) for the full workflow.
### `oxidoc update`
Update to the latest version.
```bash
oxidoc update
oxidoc update --pre
```
| Flag | Description |
|:-----|:------------|
| `--pre` | Install the latest prerelease instead of stable |
### `oxidoc set-version`
Switch to a specific version.
```bash
oxidoc set-version v0.1.0
```
Downloads and installs the exact version specified.
## Examples
Run dev server from a different directory:
```bash
oxidoc -C ../my-docs dev
```
Production build with verbose output to a custom directory:
```bash
oxidoc -v build -o public
```
Quiet build for CI:
```bash
oxidoc -q build
```
---
# Built-in Components — Callouts, Tabs, Cards, and More (docs/components)
---
title: Built-in Components — Callouts, Tabs, Cards, and More
description: Explore every built-in Oxidoc component with live examples. Callouts, tabs, code blocks, accordions, cards, badges, tooltips, banners, and Mermaid diagrams.
---
# Components
Oxidoc ships a set of built-in components you can use directly in `.rdx` files. Components use HTML-like syntax and can contain Markdown content.
## Callout
Highlighted blocks for notes, warnings, tips, and errors.
| Prop | Type | Default | Description |
|:-----|:-----|:--------|:------------|
| `kind` | String | `"info"` | Style: `info`, `warning`, `error`/`danger`, `tip`/`success` |
| `title` | String | — | Header text |
| `collapsible` | bool | `false` | Make the callout collapsible |
This is an informational callout.
This is a helpful tip.
Something to be careful about.
This highlights an error or danger.
## Tabs
Switchable content panels. Selection persists via localStorage when a `group` is set.
| Prop | Type | Description |
|:-----|:-----|:------------|
| `group` | String | localStorage key for persisting selection across pages |
Each `Tab` takes a `title` prop.
```bash
npm install my-package
```
```bash
yarn add my-package
```
```bash
pnpm add my-package
```
## CodeBlock
Syntax-highlighted code with optional header and line highlighting.
| Prop | Type | Default | Description |
|:-----|:-----|:--------|:------------|
| `language` | String | — | Language for syntax highlighting |
| `filename` | String | — | Filename displayed in the header |
| `highlight` | String | — | Line numbers to highlight (e.g., `"2"`, `"1,3-5"`) |
| `line_numbers` | bool | `false` | Show line numbers |
Includes a copy-to-clipboard button.
fn main() {
println!("Hello, Oxidoc!");
}
## Accordion
Collapsible sections.
| Prop | Type | Default | Description |
|:-----|:-----|:--------|:------------|
| `title` | String | — | Trigger text |
| `multiple` | bool | `false` | Allow multiple items open simultaneously |
Hidden content goes here. You can put any content inside an accordion.
More hidden content.
## CardGrid
Responsive grid of cards.
| Prop | Type | Default | Description |
|:-----|:-----|:--------|:------------|
| `columns` | Number | `3` | Number of columns (1-6) |
Each `Card` supports:
| Prop | Type | Description |
|:-----|:-----|:------------|
| `title` | String | Card title |
| `icon` | String | Emoji or icon |
| `href` | String | Makes the card a link |
Learn the basics of Oxidoc.
Explore all available components.
Customize your documentation site.
## Steps
Numbered step-by-step instructions with a connecting line.
Each `Step` takes a `title` prop.
Run the install script.
Run `oxidoc init my-docs`.
Create `.rdx` files and run `oxidoc dev`.
## Badge
Inline status pill.
| Prop | Type | Default | Description |
|:-----|:-----|:--------|:------------|
| `variant` | String | `"info"` | Color: `info`, `tip`, `warning`, `danger`, `new`, `deprecated` |
| `outline` | bool | `false` | Outline style |
This feature is New in v0.1.0.
The old API is Deprecated.
Status: StableBetaExperimental
## Tooltip
Hover/focus popup for definitions.
| Prop | Type | Description |
|:-----|:-----|:------------|
| `text` | String | Tooltip content |
Oxidoc uses Wasm for interactive components.
## Tag
Uppercase inline label for categorization.
| Prop | Type | Default | Description |
|:-----|:-----|:--------|:------------|
| `variant` | String | `"info"` | Color: `info`, `tip`, `warning`, `danger`, `new`, `experimental`, `deprecated` |
newexperimentaldeprecated
## ThemedImage
Swaps images based on color scheme.
| Prop | Type | Description |
|:-----|:-----|:------------|
| `light` | String | Image URL for light theme |
| `dark` | String | Image URL for dark theme |
| `alt` | String | Alt text |
| `width` | String | Width |
| `height` | String | Height |
## Banner
Dismissible announcement bar.
| Prop | Type | Default | Description |
|:-----|:-----|:--------|:------------|
| `id` | String | — | Unique ID for persistence |
| `persist` | String | `"none"` | `"none"`, `"session"`, or `"forever"` |
| `dismissible` | bool | `true` | Show close button |
This is a banner announcement.
## Embed
Embed external content (iframe).
| Prop | Type | Description |
|:-----|:-----|:------------|
| `src` | String | URL to embed |
| `width` | String | Width |
| `height` | String | Height |
## Mermaid Diagrams
Use `mermaid` as the language for fenced code blocks:
```mermaid
graph LR
A[Write .rdx] --> B[oxidoc build]
B --> C[Static HTML]
B --> D[Wasm Islands]
C --> E[Deploy]
D --> E
```
No configuration needed — diagrams render automatically.
## Landing Page Components
For `Hero`, `FeatureGrid`, `Section`, `CTA`, and other landing page components, see [Landing Page Components](/docs/components/landing-page).
---
# Landing Page Components — Hero, Features, CTA (docs/components/landing-page)
---
title: Landing Page Components — Hero, Features, CTA
description: Build stunning landing pages with Oxidoc's Hero, FeatureGrid, Section, CTA, and Testimonial components. Full-width layout with zero custom code.
---
# Landing Page Components
These components are designed for full-width landing pages. Set `layout: landing` in your frontmatter to use them.
```yaml
---
title: My Project
layout: landing
---
```
The `landing` layout removes the sidebar and table of contents, giving components the full page width.
## Hero
The main banner at the top of a landing page.
| Prop | Type | Description |
|:-----|:-----|:------------|
| `title` | String | Large heading text |
Use `HeroAction` children for call-to-action buttons and include body text as the tagline.
```markdown
A short tagline describing your project.
```
### HeroAction
Buttons inside a Hero.
| Prop | Type | Description |
|:-----|:-----|:------------|
| `label` | String | Button text |
| `href` | String | Link URL |
| `variant` | String | `"primary"` (filled) or `"secondary"` (outlined) |
## Section
A full-width content section with optional background.
| Prop | Type | Description |
|:-----|:-----|:------------|
| `bg` | String | Background style: `"muted"` for subtle contrast, omit for default |
```markdown
Content goes here.
```
## FeatureGrid
A responsive grid of feature cards. Wrap `Feature` components inside it.
```markdown
Description of feature.
Another feature.
```
### Feature
A single feature card inside a `FeatureGrid`.
| Prop | Type | Description |
|:-----|:-----|:------------|
| `icon` | String | Emoji or icon |
| `title` | String | Feature title |
| `class` | String | Optional CSS class for animations |
## CTA
Call-to-action section. Use `HeroAction` children for buttons.
| Prop | Type | Description |
|:-----|:-----|:------------|
| `title` | String | CTA heading |
| `description` | String | Supporting text |
```markdown
```
## TestimonialGrid
A grid of testimonial cards.
```markdown
This tool is amazing.
Saved us hours of work.
```
### Testimonial
A single testimonial card.
| Prop | Type | Description |
|:-----|:-----|:------------|
| `name` | String | Person's name |
| `role` | String | Title or role |
| `avatar` | String | Avatar image URL |
## Full Example
---
title: My Project
layout: landing
---
Build something great with zero configuration.
Lightning-fast builds.Built-in full-text search.Zero config to get started.
---
# Search - Built-in Hybrid Search Engine (docs/search)
---
title: Search - Built-in Hybrid Search Engine
description: Oxidoc ships a production-grade search engine running in-browser via Wasm. BM25 lexical search works with zero config. Add semantic search with one line.
---
# Search
Oxidoc ships a production-grade search engine that runs entirely in the browser via WebAssembly. No server, no external service, no API keys — just build your site and search works.
BM25 keyword search with fuzzy matching, phrase boost, and section-level results. Zero configuration.
Understand the meaning of queries with sentence embeddings. Enable with one config line.
Use your own GGUF embedding model for non-English docs or specialized domains.
Integrate Algolia, Typesense, Meilisearch, or bring your own search service.
## Overview
| Feature | Status | Details |
|:--------|:-------|:--------|
| Lexical (BM25) | Enabled by default | Zero config, always works |
| Semantic embeddings | Opt-in | `semantic = true` in config |
| Hybrid ranking (RRF) | Automatic | When semantic is enabled, fuses both result sets |
| Fuzzy matching | Built-in | Tolerates typos based on term length |
| Section-level results | Built-in | Links to the exact heading, not just the page |
| Lazy chunk loading | Built-in | Only fetches index data matching query terms |
| Custom models | Supported | Provide your own GGUF sentence embedding model |
| External providers | Supported | Algolia, Typesense, Meilisearch, or custom JS |
## How It Works
```mermaid
graph TD
B[oxidoc build] --> L[BM25 Index]
B --> S[Embedding Vectors]
B --> M[Model Copy]
L --> C[Chunked by term prefix]
C --> W[Browser / Wasm]
S --> W
M --> W
W --> Q[User Query]
Q --> LR[Lexical Results]
Q --> SR[Semantic Results]
LR --> RRF[Reciprocal Rank Fusion]
SR --> RRF
RRF --> R[Ranked Results]
```
At build time, Oxidoc generates a search index from all your pages. The index is split into small chunks so the browser only downloads what's needed for each query. When semantic search is enabled, pre-computed embedding vectors and the model file are also included in the output.
## Quick Start
Search works out of the box. To enable semantic search:
[search]
semantic = true
That's it. See the subpages for deep dives into each feature.
## Output Files
When using the built-in provider, `oxidoc build` generates these search files in `dist/`:
| File | Purpose | Loaded |
|:-----|:--------|:-------|
| `search-meta.bin` | Document metadata + chunk manifest | Always (on page load) |
| `search-chunk-{id}.bin` | Lexical postings for term prefixes | On demand (per query) |
| `search-vectors.json` | Pre-computed page embeddings | Only if semantic enabled |
| `search-model.gguf` | Embedding model for query-time inference | Only if semantic enabled |
---
# Lexical Search — BM25 Full-Text Search Engine (docs/search/lexical)
---
title: Lexical Search — BM25 Full-Text Search Engine
description: Oxidoc's built-in BM25 search engine with fuzzy matching, phrase boost, section-level results, and lazy chunk loading. Works with zero configuration.
---
# Lexical Search
Lexical search is Oxidoc's default search engine. It uses BM25 — the same ranking algorithm behind Elasticsearch and Apache Lucene — to match pages by keyword relevance. It works out of the box with zero configuration.
## Features
| Feature | Description |
|:--------|:------------|
| **BM25 scoring** | Industry-standard ranking with K1=1.2, B=0.75 tuning |
| **Fuzzy matching** | Levenshtein edit distance — tolerates typos automatically |
| **Prefix matching** | Results appear as you type, before finishing the word |
| **Phrase boost** | 5x score boost when query terms appear consecutively in content |
| **Heading boost** | 2x score boost for matches in page headings |
| **Section scoring** | Results link to the exact heading section, not just the page |
| **CamelCase splitting** | "CodeBlock" matches searches for "code" or "block" |
| **Breadcrumb trails** | Results show "Page > H2 > H3" navigation path |
| **Context snippets** | 160-character excerpt around the match, aligned to word boundaries |
| **Lazy chunk loading** | Only downloads index chunks matching the query's term prefixes |
## How BM25 Works
BM25 (Best Matching 25) scores each page based on how often the query terms appear, normalized by document length:
- **Term frequency** — pages where the term appears more often score higher, with diminishing returns (saturation at K1=1.2)
- **Inverse document frequency** — rare terms are worth more than common ones
- **Length normalization** — short, focused pages aren't penalized against long ones (B=0.75)
This means a concise page that mentions "versioning" 3 times ranks higher than a sprawling page that mentions it once in passing.
## Fuzzy Matching
Oxidoc tolerates typos automatically based on term length:
| Term Length | Max Edits | Example |
|:------------|:----------|:--------|
| 1–3 chars | 0 | "css" → exact match only |
| 4–6 chars | 1 | "buld" → matches "build" |
| 7+ chars | 2 | "conifgure" → matches "configure" |
Fuzzy matching kicks in when an exact match isn't found. You don't need to configure it.
## Section-Level Results
Results don't just link to a page — they link to the specific heading section where the match was found. Each result includes:
- **Anchor link** — clicking goes directly to the matching section
- **Breadcrumb trail** — shows the heading hierarchy (e.g., "Configuration > Theme > Dark Mode")
- **Context snippet** — 160-character excerpt from the matching section
## Lazy Chunk Loading
The search index is split into chunks by 2-character term prefix (e.g., "co", "se", "bu"). When a user types a query:
1. Oxidoc determines which chunks are needed based on the query terms
2. Only those chunks are fetched from the server
3. Previously loaded chunks are cached in memory
This means the browser never downloads the full index — only the small slices relevant to the current query. For large documentation sites, this keeps search fast regardless of total page count.
## Index Size
The lexical index is compact. For a documentation site with ~50 pages:
- `search-meta.bin` — ~20-50 KB (loaded once on page open)
- Each `search-chunk-{id}.bin` — ~1-10 KB (loaded on demand)
Total transfer per query is typically under 20 KB.
---
# Semantic Search — AI-Powered Conceptual Search (docs/search/semantic)
---
title: Semantic Search — AI-Powered Conceptual Search
description: Enable semantic search in Oxidoc to understand the meaning behind queries. Uses sentence embeddings and cosine similarity — runs in-browser via Wasm, no API keys.
---
# Semantic Search
Semantic search uses a sentence embedding model to understand the *meaning* of queries, not just keyword matches. When a user searches for "how do I change colors", it finds the theming page even if it never uses the word "colors".
## Enable Semantic Search
[search]
semantic = true
That's it. Oxidoc bundles a default embedding model — no downloads, no API keys, no external services.
## How It Works
### Build Time
1. Oxidoc loads the embedding model (bundled BGE Micro v2 or your custom model)
2. Every page's text content is embedded into a 384-dimensional vector
3. The vectors are written to `search-vectors.json` in the output
4. The model file is copied to `search-model.gguf` in the output
### Query Time (In Browser)
1. The browser fetches the model file and pre-computed vectors
2. The model initializes in Wasm (CPU-only, no GPU)
3. When the user triggers semantic search, the query text is embedded in real-time
4. Cosine similarity is computed between the query vector and all page vectors
5. Results are ranked by similarity and merged with lexical results via RRF
All computation happens in the browser. No data leaves the user's machine.
## Hybrid Ranking
When semantic search is enabled, Oxidoc runs **both** engines on every "Ask AI" query and merges results using **Reciprocal Rank Fusion (RRF)**:
```mermaid
graph LR
Q[User Query] --> L[Lexical / BM25]
Q --> S[Semantic / Cosine Similarity]
L --> F[RRF Fusion]
S --> F
F --> R[Ranked Results]
```
| Engine | Weight | Strengths |
|:-------|:-------|:----------|
| Lexical (BM25) | **70%** | Exact terms, function names, config keys, error messages |
| Semantic | **30%** | Conceptual queries, synonyms, "how do I..." questions, paraphrasing |
Pages appearing in both result sets get combined scores. A page that matches keywords *and* is semantically relevant ranks highest.
### Why 70/30?
Documentation search is keyword-heavy — users often search for exact function names, config fields, or error messages. The 70% lexical weight ensures these exact matches always surface. The 30% semantic weight adds recall for conceptual queries without drowning out precise keyword results.
## The Default Model
Oxidoc bundles **BGE Micro v2** — a compact sentence embedding model by BAAI:
| Property | Value |
|:---------|:------|
| Model | [BAAI/bge-micro-v2](https://huggingface.co/aapot/bge-micro-v2-GGUF) |
| Format | GGUF (model + tokenizer in one file) |
| Size | 17.5 MB |
| Dimensions | 384 |
| Runtime | CPU-only (no GPU required) |
| Language | English (primary) |
### Why This Model?
At 17.5 MB, the model is embedded directly in the Oxidoc binary. No download step during install or build.
The same 17.5 MB file is served to browsers for query-time inference. Users don't wait for a 500 MB download before search works.
BGE Micro v2 produces high-quality embeddings for English technical content — documentation, tutorials, API references.
Runs on any device. No GPU, no WebGPU, no special hardware. Works on phones, tablets, old laptops.
### Limitations
- **Optimized for English** — for non-English documentation, use a [custom model](/docs/ai/custom-models)
- **384 dimensions** — larger models with higher dimensions may capture more nuance, at the cost of size
- **17.5 MB browser download** — users on slow connections may experience a delay before semantic search is available (lexical search works immediately while the model loads)
## Build-Time vs Query-Time
| Phase | What Happens | Time |
|:------|:-------------|:-----|
| **Build** | Embed all pages, write vectors + copy model | Seconds (CPU) |
| **Page load** | Fetch `search-meta.bin` | Instant (~20 KB) |
| **Semantic init** | Fetch model (17.5 MB) + vectors | 1–3 seconds |
| **Regular search** | BM25 only (no model needed) | <10 ms |
| **AI search** | Embed query + cosine similarity + RRF fusion | 50–200 ms |
Lexical search is available immediately on page load. Semantic search becomes available once the model finishes loading in the background. Users are never blocked — they can search right away with keywords while the model downloads.
## Configuration Reference
[search]
# Enable semantic search (default: false)
semantic = true
# Custom GGUF model (overrides bundled BGE Micro v2)
# model_path = "./models/my-model.gguf"
| Field | Type | Default | Description |
|:------|:-----|:--------|:------------|
| `semantic` | bool | `false` | Enable semantic search alongside BM25 |
| `model_path` | String | — | Path to a custom GGUF embedding model |
When `model_path` is set, Oxidoc uses your model instead of the bundled one. See [Custom Models](/docs/ai/custom-models) for details.
## Embedding Output Format
When semantic search is enabled, `oxidoc build` writes `search-vectors.json` to your output directory. This is a plain JSON file containing every page's embedding:
{
"dimension": 384,
"documents": [
{
"id": 0,
"title": "Installation",
"path": "/docs/installation",
"snippet": "Install Oxidoc with a single command on Linux, macOS, or Windows...",
"text": "The recommended way to install Oxidoc is the install script...",
"headings": [
{ "title": "Install Script", "anchor": "install-script", "depth": 2, "offset": 0 },
{ "title": "GitHub Releases", "anchor": "github-releases", "depth": 2, "offset": 312 }
]
}
],
"vectors": [
[0.0231, -0.0412, 0.0889, "... 384 floats total ..."]
]
}
Each entry in `vectors` corresponds to the document at the same index in `documents`. The vectors are 32-bit floats, one per dimension (384 for the default model).
### Using Embeddings in Your Own RAG Pipeline
The `search-vectors.json` file is a portable artifact you can use outside of Oxidoc. After running `oxidoc build`, copy the file and feed it into any vector database or RAG pipeline:
import json
import numpy as np
with open("dist/search-vectors.json") as f:
data = json.load(f)
vectors = np.array(data["vectors"], dtype=np.float32)
docs = data["documents"]
# Compute cosine similarity with a query embedding
query = your_model.encode("how do I configure search?")
similarities = vectors @ query / (np.linalg.norm(vectors, axis=1) * np.linalg.norm(query))
# Top 5 results
top_indices = np.argsort(similarities)[::-1][:5]
for idx in top_indices:
print(f"{docs[idx]['title']}: {similarities[idx]:.4f}")
import json
import chromadb
with open("dist/search-vectors.json") as f:
data = json.load(f)
client = chromadb.Client()
collection = client.create_collection("docs")
collection.add(
ids=[str(d["id"]) for d in data["documents"]],
embeddings=data["vectors"],
documents=[d["text"] for d in data["documents"]],
metadatas=[{"title": d["title"], "path": d["path"]} for d in data["documents"]],
)
# Query
results = collection.query(query_texts=["how to deploy"], n_results=5)
const data = JSON.parse(fs.readFileSync("dist/search-vectors.json", "utf-8"));
// Each vector is a Float32 array of `dimension` length
const { documents, vectors, dimension } = data;
// Feed into Pinecone, Weaviate, Qdrant, etc.
for (let i = 0; i < documents.length; i++) {
await vectorDB.upsert({
id: documents[i].path,
values: vectors[i],
metadata: {
title: documents[i].title,
snippet: documents[i].snippet,
},
});
}
### What's Included Per Document
| Field | Description |
|:------|:------------|
| `id` | Numeric index (0-based) |
| `title` | Page title (from first `
`) |
| `path` | URL path (e.g., `/docs/installation`) |
| `snippet` | First 160 characters of content |
| `text` | Full plain text content (markdown stripped) |
| `headings` | Heading positions with title, anchor, depth, and character offset |
The `text` and `headings` fields give you everything needed to build section-level retrieval — split text by heading offsets to create chunks mapped to specific sections.
For RAG pipelines, combine `search-vectors.json` (pre-computed embeddings) with `llms-full.txt` (full text) and `llms.txt` (page index). All three are generated automatically on every build — your documentation is RAG-ready out of the box.
---
# External Search Providers — Algolia, Typesense, Meilisearch (docs/search/providers)
---
title: External Search Providers — Algolia, Typesense, Meilisearch
description: Integrate external search providers with Oxidoc. Step-by-step setup for Algolia DocSearch, Typesense, Meilisearch, or bring your own custom search solution.
---
# External Search Providers
Oxidoc's built-in search handles most documentation sites. For larger sites, existing infrastructure, or specific requirements, you can use an external search provider instead.
When you set an external provider, Oxidoc skips generating the built-in search index and injects the provider's scripts and styles into your pages.
## Algolia DocSearch
[Algolia DocSearch](https://docsearch.algolia.com/) is a popular hosted search for documentation sites. It's free for open-source projects.
[search]
provider = "algolia"
app_id = "YOUR_APP_ID"
api_key = "YOUR_SEARCH_API_KEY"
index_name = "your_index"
| Field | Description |
|:------|:------------|
| `app_id` | Your Algolia application ID |
| `api_key` | **Search-only** API key (never use your admin key) |
| `index_name` | Name of the Algolia index to search |
Go to [docsearch.algolia.com](https://docsearch.algolia.com/) and apply. Open-source projects get free indexing and hosting.
After approval, Algolia provides your `app_id`, `api_key`, and `index_name`.
Add the credentials to your `[search]` config as shown above.
Algolia's crawler indexes your deployed site automatically. Search works after the first crawl completes.
Use the **search-only** API key in your config, not the admin key. The API key is visible in your site's HTML source. The search-only key can only read — it cannot modify your index.
## Typesense
[Typesense](https://typesense.org/) is an open-source, typo-tolerant search engine you can self-host or use as a cloud service.
[search]
provider = "typesense"
host = "your-typesense-host.com"
port = 443
protocol = "https"
api_key = "YOUR_SEARCH_API_KEY"
collection_name = "docs"
| Field | Description |
|:------|:------------|
| `host` | Typesense server hostname |
| `port` | Server port (typically 443 for HTTPS, 8108 for local) |
| `protocol` | `"https"` or `"http"` |
| `api_key` | Search-only API key |
| `collection_name` | Name of the Typesense collection |
Self-host with Docker or use [Typesense Cloud](https://cloud.typesense.org/).
```bash
docker run -p 8108:8108 -v /tmp/typesense-data:/data typesense/typesense:latest \
--data-dir /data --api-key=your-admin-key
```
Use the Typesense API or a scraper like [typesense-docsearch-scraper](https://github.com/typesense/typesense-docsearch-scraper) to index your built site.
Add your Typesense credentials as shown above. Use a search-only API key.
## Meilisearch
[Meilisearch](https://www.meilisearch.com/) is an open-source, fast search engine with typo tolerance and filtering.
[search]
provider = "meilisearch"
host = "https://your-meilisearch-host.com"
api_key = "YOUR_SEARCH_API_KEY"
index_name = "docs"
| Field | Description |
|:------|:------------|
| `host` | Meilisearch instance URL (including protocol) |
| `api_key` | Search-only API key |
| `index_name` | Name of the Meilisearch index |
Self-host with Docker or use [Meilisearch Cloud](https://www.meilisearch.com/cloud).
```bash
docker run -p 7700:7700 -v /tmp/meili-data:/meili_data getmeili/meilisearch:latest
```
Use the Meilisearch API or [docs-scraper](https://github.com/meilisearch/docs-scraper) to index your built site.
Add your Meilisearch credentials as shown above.
## Custom Provider
For any search service not listed above, inject your own CSS and JavaScript:
[search]
provider = "custom"
stylesheet = "https://cdn.example.com/search.css"
script = "https://cdn.example.com/search.js"
init_script = "assets/js/search-init.js"
| Field | Type | Description |
|:------|:-----|:------------|
| `stylesheet` | String | CSS file loaded in `` via `` tag |
| `script` | String | JS file loaded at end of `` via `
```
All of this is generated from your page content and `oxidoc.toml` config — no manual meta tags needed.
## Page Title and Description
### Title
The page title comes from the first `
` heading in your content. It's used for ``, `og:title`, `twitter:title`, and JSON-LD `name`.
Write titles that are:
- Under 60 characters to avoid truncation in search results
- Front-loaded with the primary keyword
- Unique across all pages
### Description
The meta description is **auto-extracted from the first paragraph** of your page content, truncated to 160 characters.
Write your opening paragraph with SEO in mind — it becomes your search snippet. Make it:
- 150–160 characters with a clear summary of the page
- Action-oriented ("Learn how to...", "Configure...", "Set up...")
- Unique per page to avoid internal competition
Since Oxidoc uses your first paragraph as the meta description, craft it like a micro-advertisement for the page. This is what appears in Google results and social shares.
## Open Graph Tags
Open Graph tags control how your pages appear when shared on social platforms (Facebook, LinkedIn, Slack, Discord).
Oxidoc generates these automatically:
| Tag | Source |
|:----|:-------|
| `og:title` | First `
` heading |
| `og:description` | First paragraph (160 chars) |
| `og:url` | `base_url` + page path |
| `og:type` | `article` |
| `og:site_name` | `project.name` from config |
### Adding og:image
Oxidoc doesn't generate `og:image` automatically, but you can add it per-page using the `` component:
# Introduction
Your page content here.
For a site-wide social image, add it to every page or create a shared component.
## Twitter Cards
Oxidoc generates `twitter:card` (set to `summary`) and `twitter:title` for every page. Add more Twitter meta tags with ``:
```markdown
```
## The `` Component
The `` component lets you inject any HTML into the `` of a page. Use it for custom meta tags, preload hints, alternate language links, or anything Oxidoc doesn't generate automatically.
```markdown
```
Content inside `` is moved from the body to `` during rendering. Only raw HTML is supported — no components inside ``.
## JSON-LD Structured Data
Every page includes a `WebPage` JSON-LD block with `@context`, `@type`, `name`, `description`, `url`, and `site.name`. This helps search engines and AI systems understand your content structure.
The structured data is generated from:
- Page title (from `
`)
- Page description (from first paragraph, or `project.description`)
- Full URL (from `base_url` + slug)
- Site name (from `project.name`)
## Canonical URLs
When `base_url` is set in `oxidoc.toml`, every page gets a `` tag. This prevents duplicate content issues when the same page is accessible at multiple URLs.
[project]
base_url = "https://example.com"
## Auto-Generated SEO Files
### sitemap.xml
Generated automatically with URLs for every page in your navigation. Search engines use this to discover and index your content.
### robots.txt
Generated with permissive defaults:
```text
User-agent: *
Allow: /
Sitemap: https://example.com/sitemap.xml
```
### Atom Feed (feed.xml)
An Atom feed of all documentation pages. Each entry includes the first paragraph as a summary. Useful for RSS readers and content aggregators.
### llms.txt and llms-full.txt
Machine-readable files for AI tools and RAG pipelines. See [llms.txt](/docs/ai/llms-txt) for details.
## Versioned Content and SEO
Archived (non-default) versions automatically get:
```html
```
This prevents search engines from indexing outdated documentation while keeping it accessible to users who navigate to it directly.
## Edit Links
Connect each page to its source for transparency and community contributions:
[project]
edit_url = "https://github.com/org/repo/blob/main"
edit_label = "Edit this page"
Each page shows a link to its source file, encouraging contributions and signaling content freshness.
## Redirects
Set up redirects for moved or renamed pages to preserve link equity:
[redirects]
redirects = [
{ from = "/old-page", to = "/new-page" },
]
Each redirect generates an HTML file with ``.
## SEO Checklist
Use this checklist when writing documentation pages:
This becomes your meta description. Keep it under 160 characters, action-oriented, and unique per page.
The `
` becomes your ``, `og:title`, and JSON-LD name. Front-load the primary keyword.
Required for canonical URLs, sitemap, feeds, and absolute OG URLs.
Use the `` component to add social sharing images, especially for landing pages and high-traffic docs.
Every page must have a unique `
` to avoid competing with your own pages in search results.
---
# Analytics — Google Analytics and Custom Scripts (docs/analytics)
---
title: Analytics — Google Analytics and Custom Scripts
description: Add analytics to your Oxidoc site. Configure Google Analytics with one line, or inject custom scripts for Plausible, Fathom, Umami, and other providers.
---
# Analytics
Oxidoc supports Google Analytics and custom analytics scripts.
## Google Analytics
Add your measurement ID:
[analytics]
google_analytics = "G-XXXXXXXXXX"
Oxidoc injects the Google Analytics gtag.js snippet into every page.
## Custom Script
For other analytics providers (Plausible, Fathom, Umami, etc.), inject a custom script tag:
[analytics]
script = ''
The `script` value is injected directly into the `` of every page. You can use any analytics provider that works via a script tag.
## Using Both
You can set both `google_analytics` and `script` if needed — both will be injected.
---
# Theming - CSS Variables, Dark Mode, and Custom Styles (docs/theming)
---
title: Theming - CSS Variables, Dark Mode, and Custom Styles
description: Customize every visual aspect of your Oxidoc site. Override 50+ CSS variables for colors, typography, layout, shadows, and transitions. Dark mode included.
---
# Theming
Oxidoc is styled entirely with CSS custom properties (`--oxidoc-*` variables). Override any variable to customize your site — from a single color change to a complete visual redesign.
## Quick Config
For simple changes, set values directly in `oxidoc.toml`:
[theme]
primary = "#8b5cf6"
accent = "#f59e0b"
font = "Inter, sans-serif"
code_font = "JetBrains Mono, monospace"
dark_mode = "system"
| Field | What it controls |
|:------|:----------------|
| `primary` | Links, active sidebar items, buttons, callout info borders |
| `accent` | Accent highlights and decorative elements |
| `font` | Body text font stack |
| `code_font` | Code blocks and inline code font stack |
| `dark_mode` | `"system"` (default), `"light"`, or `"dark"` |
These fields are convenience shortcuts. For full control, use custom CSS.
## Dark Mode
The `dark_mode` setting controls initial behavior:
- `"system"` — follows OS preference via `prefers-color-scheme`, with a toggle in the header
- `"light"` — always light mode
- `"dark"` — always dark mode
In `system` mode, users can toggle manually. The preference is stored in the browser.
## Custom CSS
Load one or more CSS files that override any `--oxidoc-*` variable:
[theme]
custom_css = ["assets/variables.css", "assets/overrides.css"]
Files are concatenated in order. All Oxidoc styles are wrapped in `@layer oxidoc`, so your custom CSS **always wins** the cascade — no `!important` needed.
## CSS Variable Reference
Every visual property in Oxidoc is controlled by a CSS variable. Override them in `:root` for light mode and `[data-theme="dark"]` for dark mode.
### Core Colors
:root {
--oxidoc-primary: #2563eb;
--oxidoc-accent: #f59e0b;
--oxidoc-bg: #ffffff;
--oxidoc-bg-secondary: #f8fafc;
--oxidoc-text: #1e293b;
--oxidoc-text-secondary: #64748b;
--oxidoc-border: #e2e8f0;
--oxidoc-code-bg: #f1f5f9;
}
[data-theme="dark"] {
--oxidoc-primary: #3b82f6;
--oxidoc-accent: #fbbf24;
--oxidoc-bg: #0f172a;
--oxidoc-bg-secondary: #1e293b;
--oxidoc-text: #e2e8f0;
--oxidoc-text-secondary: #94a3b8;
--oxidoc-border: #334155;
--oxidoc-code-bg: #1e293b;
}
### Primary Color Shades
Auto-generated from your primary color using `color-mix()`:
```css
:root {
--oxidoc-primary-light /* 70% primary + white */
--oxidoc-primary-dark /* 70% primary + black */
--oxidoc-primary-lighter /* 40% primary + white */
--oxidoc-primary-darker /* 40% primary + black */
}
```
These adapt automatically when you change `--oxidoc-primary`.
### Semantic Colors
Used by callouts, badges, tags, API method badges, and status indicators:
:root {
--oxidoc-success: #10b981; --oxidoc-success-text: #059669;
--oxidoc-warning: #f59e0b; --oxidoc-warning-text: #b45309;
--oxidoc-error: #ef4444; --oxidoc-error-text: #dc2626;
--oxidoc-info: #3b82f6; --oxidoc-info-text: #2563eb;
--oxidoc-new: #8b5cf6; --oxidoc-new-text: #7c3aed;
--oxidoc-deprecated: #6b7280; --oxidoc-deprecated-text: #4b5563;
}
All semantic colors have dark mode variants applied automatically.
### Utility Colors
:root {
--oxidoc-text-muted: #6b7280;
--oxidoc-bg-subtle: #f3f4f6;
--oxidoc-on-primary: #fff;
--oxidoc-shadow: rgba(0, 0, 0, 0.1);
--oxidoc-overlay: rgba(0, 0, 0, 0.4);
}
### Callout Customization
Each callout type exposes its own variables:
.oxidoc-callout-warning {
--oxidoc-callout-color: #ea580c;
--oxidoc-callout-bg: #fff7ed;
--oxidoc-callout-border: #ea580c;
--oxidoc-callout-text: #1e293b;
}
### Typography
:root {
--oxidoc-font-sans: system-ui, -apple-system, sans-serif;
--oxidoc-font-mono: "SF Mono", "Fira Code", monospace;
}
### Layout
:root {
--oxidoc-content-max: 48rem;
--oxidoc-sidebar-width: 16rem;
--oxidoc-toc-width: 14rem;
--oxidoc-header-height: 3.5rem;
}
### Border Radius
:root {
--oxidoc-radius-sm: 0.25rem;
--oxidoc-radius-md: 0.375rem;
--oxidoc-radius-lg: 0.5rem;
}
### Shadows
:root {
--oxidoc-shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.05);
--oxidoc-shadow-md: 0 4px 12px rgba(0, 0, 0, 0.1);
--oxidoc-shadow-lg: 0 12px 36px rgba(0, 0, 0, 0.15);
}
### Z-Index
:root {
--oxidoc-z-tooltip: 10;
--oxidoc-z-back-to-top: 50;
--oxidoc-z-sidebar: 90;
--oxidoc-z-header: 100;
--oxidoc-z-skip-nav: 200;
--oxidoc-z-overlay: 1000;
}
### Transitions
:root {
--oxidoc-transition-fast: 0.15s ease;
--oxidoc-transition-normal: 0.25s ease;
--oxidoc-transition-slow: 0.4s ease;
--oxidoc-transition-spring: 0.5s cubic-bezier(0.19, 1, 0.22, 1);
}
## Community Themes
Community themes are `.css` files that override `--oxidoc-*` variables. To use one:
[theme]
custom_css = ["assets/dracula.css"]
To create a theme, write a CSS file that sets the variables above for both `:root` (light) and `[data-theme="dark"]` (dark), then share it.
---
# API Reference — OpenAPI Integration Guide (docs/api-reference)
---
title: API Reference — OpenAPI Integration Guide
description: Generate interactive API documentation from OpenAPI specs. Auto-generated parameter tables, response schemas, and a live Wasm-powered API playground.
---
# API Reference
Oxidoc generates interactive API documentation directly from your OpenAPI specification. Drop a YAML or JSON spec into your project and Oxidoc does the rest — parameter tables, request/response schemas, and a live API playground on every endpoint page.
Click **API** in the header or go to [/api](/api) to see the generated pages with the interactive playground.
## Setup
Add a navigation entry with `openapi` pointing to your spec:
[routing]
navigation = [
{ path = "/api", openapi = "./openapi.yaml" },
]
Run `oxidoc dev` or `oxidoc build`. Oxidoc parses the spec, generates a page for every endpoint, groups them by tag in the sidebar, and creates an index page at `/api/`.
## What Gets Generated
For each endpoint in the spec, Oxidoc creates a page with:
Color-coded HTTP method badge with the endpoint path.
Auto-generated table of query, path, and header parameters with types and descriptions.
Schema display for endpoints that accept a request body, with content type.
All documented response codes with their descriptions and schemas.
Interactive Wasm-powered playground to fill in parameters and test the endpoint live.
## Endpoint Grouping
Endpoints are grouped by their OpenAPI `tags`. Each tag becomes its own sidebar section. Untagged endpoints appear under a default group.
## The API Playground
Every endpoint page includes an interactive playground powered by WebAssembly:
- Pre-fills parameters from the spec (name, type, required)
- Supports path, query, and header parameters
- Sends real HTTP requests to your configured base URL
- Shows response status, headers, and body
- Generates code snippets for `curl`, `fetch`, and other clients
Set `servers` in your OpenAPI spec to configure the target URL for the playground. The first server entry is used by default.
## Supported Features
| Feature | Status |
|:--------|:-------|
| OpenAPI 3.0 | Supported |
| OpenAPI 3.1 | Supported |
| Path parameters | Supported |
| Query parameters | Supported |
| Header parameters | Supported |
| Request body schemas | Supported |
| Response schemas | Supported |
| Multiple tags | Supported |
| `$ref` references | Supported |
| Deprecation notices | Supported |
| Authentication schemes | Coming soon |
| Webhook endpoints | Coming soon |
---
# Animations — CSS Keyframes, Scroll Triggers, and Stagger (docs/guides/animations)
---
title: Animations — CSS Keyframes, Scroll Triggers, and Stagger
description: Add animations to your Oxidoc site. 15 built-in keyframes, scroll-triggered entrances, stagger delays, and animatable CSS variables — pure CSS, no JS.
---
# Animations
Oxidoc ships a built-in animation system — 15 reusable `@keyframes`, scroll-triggered entrances, and animatable CSS variables. Pure CSS, no JavaScript required.
## Built-in Component Animations
Landing page components animate automatically:
- **Hero** — title fades down, tagline and actions fade up with stagger
- **Features** — each card fades in from below, staggered by position
- **Testimonials** — cards fade in with stagger
- **CTA** — fades in from below
- **Search dialog** — overlay fades in, dialog scales in
- **Tooltips** — fade in on hover
No configuration needed — these work out of the box.
## Using Animation Classes
Components that accept a `class` prop can use animation utility classes:
```rdx
Sub-second builds.
...
```
| Class | Effect |
|:------|:-------|
| `oxidoc-animate-fade-in` | Opacity 0 → 1 |
| `oxidoc-animate-fade-in-up` | Fade in + slide up from below |
| `oxidoc-animate-bounce-in` | Bouncy entrance with overshoot |
| `oxidoc-animate-pop` | Quick scale-up pop |
| `oxidoc-animate-float` | Gentle up-and-down floating (infinite) |
| `oxidoc-animate-spin` | 360° rotation (infinite) |
| `oxidoc-animate-shimmer` | Loading skeleton shimmer (infinite) |
### Stagger Delays
Add stagger classes for sequential timing:
```rdx
.........
```
`oxidoc-stagger-1` through `oxidoc-stagger-5` add incremental delays (0.1s each by default).
## Custom CSS Animations
Use the shipped `@keyframes` in your own custom CSS:
.my-element {
animation: oxidoc-fade-in-up var(--oxidoc-transition-normal) both;
}
.my-modal {
animation: oxidoc-scale-in var(--oxidoc-transition-spring) both;
}
### Available Keyframes
| Keyframe | Description |
|:---------|:------------|
| `oxidoc-fade-in` | Opacity 0 → 1 |
| `oxidoc-fade-out` | Opacity 1 → 0 |
| `oxidoc-fade-in-up` | Fade in + translate up |
| `oxidoc-fade-in-down` | Fade in + translate down |
| `oxidoc-slide-in-left` | Slide in from left edge |
| `oxidoc-slide-in-right` | Slide in from right edge |
| `oxidoc-scale-in` | Scale up from 0.9 + fade in |
| `oxidoc-scale-out` | Scale up to 1.05 + fade out |
| `oxidoc-bounce-in` | Bouncy scale entrance |
| `oxidoc-pop` | Quick snap-in from scale 0 |
| `oxidoc-spin` | 360° rotation |
| `oxidoc-pulse` | Gentle scale pulse |
| `oxidoc-shake` | Horizontal shake |
| `oxidoc-float` | Gentle vertical float |
| `oxidoc-shimmer` | Loading skeleton shimmer |
## Scroll-Triggered Animations
In browsers that support scroll-driven animations, use the `oxidoc-animate-on-scroll` class to trigger entrance animations as elements scroll into view. In unsupported browsers, content appears normally.
## Animatable CSS Variables
Color variables are registered with `@property`, so the browser can smoothly interpolate them. This means you can animate theme colors with CSS transitions:
.my-element {
transition: --oxidoc-primary var(--oxidoc-transition-normal);
}
.my-element:hover {
--oxidoc-primary: #8b5cf6;
}
Registered animatable properties: `--oxidoc-primary`, `--oxidoc-accent`, `--oxidoc-bg`, `--oxidoc-text`, `--oxidoc-success`, `--oxidoc-warning`, `--oxidoc-error`.
## Transition Presets
All component transitions use these variables. Override them to change the feel of the entire site:
:root {
--oxidoc-transition-fast: 0.15s ease; /* Hover states */
--oxidoc-transition-normal: 0.25s ease; /* UI movements */
--oxidoc-transition-slow: 0.4s ease; /* Page-level */
--oxidoc-transition-spring: 0.5s cubic-bezier(0.19, 1, 0.22, 1); /* Elastic */
}
Set all transitions to `0s` for an instant-feel UI, or increase them for a more relaxed, cinematic feel.
## Accessibility
All animations automatically respect `prefers-reduced-motion`. When a user has reduced motion enabled in their OS, animation durations collapse to near-zero and iterations run once.
No configuration needed.
---
# Custom Web Components — Vanilla JS Escape Hatch (docs/guides/custom-components)
---
title: Custom Web Components — Vanilla JS Escape Hatch
description: Register custom Vanilla JS Web Components in Oxidoc. Write standard HTML Custom Elements and use them in your RDX content alongside built-in components.
---
# Custom Web Components
Oxidoc lets you register Vanilla JS Web Components as an escape hatch for UI that doesn't need the Wasm pipeline.
## Configuration
Map component tag names to JavaScript file paths in `oxidoc.toml`:
[components.custom]
PromoBanner = "assets/js/promo-banner.js"
FeedbackWidget = "assets/js/feedback-widget.js"
## Writing a Component
Each JS file should define and register an HTML Custom Element:
class PromoBanner extends HTMLElement {
connectedCallback() {
const message = this.getAttribute('message') || 'Check this out!';
this.innerHTML = `
${message}
`;
}
}
customElements.define('promo-banner', PromoBanner);
## Using in RDX
Use the component tag in your `.rdx` files just like any built-in component:
Custom components bypass the Wasm hydration pipeline entirely. Oxidoc renders the HTML element directly and loads your JS file with `