CLI Commands
Phosphor has three commands: build, init, and serve.
phosphor build
| Flag | Description |
|---|---|
directory, dir |
Path to the project directory containing docs.yaml and pages/. Defaults to the current directory (.). |
Builds your documentation site. Reads docs.yaml, parses all Markdown pages, generates the search index, and writes the complete site to the _site/ directory.
The build process:
- Loads and validates
docs.yaml(checks types:pagesandnavmust be lists,siteandthememust be mappings) - Verifies that
pages/directory exists and that all page paths stay within it (path traversal protection) - Reads each
.mdfile listed in thepagesarray - Parses Markdown into HTML (standard + extended components)
- Generates the search index from all headings and content
- Renders each page into the base HTML template
- Copies theme assets (CSS, JS, favicon) to
_site/assets/ - Writes the final HTML files to
_site/
Every build deletes and recreates the _site/ directory from scratch. This ensures no stale files remain from previous builds. The _site/ directory should be in your .gitignore.
phosphor init
| Flag | Description |
|---|---|
directory, dir |
Target directory for the new docs project. Defaults to the current directory (.). Created if it doesn't exist. |
Scaffolds a new documentation project. Creates a docs.yaml config file and example Markdown pages to get you started immediately.
If you run phosphor init inside a Git repository with a remote origin, it automatically detects the GitHub URL and populates docs.yaml with sensible defaults:
- title: Repository name, title-cased (e.g.,
my-cli-toolbecomesMy CLI Tool) - tagline:
~/repo-name - logo_text: First two characters of the repo name, uppercase
- github: Full GitHub URL
You can always edit these afterward.
Key behaviors:
- Won't overwrite: Existing files are never overwritten. If
docs.yamlor a page file already exists, it's skipped with a message - Creates directories: The target directory and
pages/subdirectory are created if they don't exist - Example content: The scaffolded pages include working examples of various components so you can see the syntax in action
phosphor serve
| Flag | Description |
|---|---|
directory, dir |
Path to the project directory containing docs.yaml. Defaults to the current directory. |
--port, -p |
Port number for the local HTTP server (1-65535). Defaults to 8000. |
Builds the site and starts a local HTTP server for previewing. This is a convenience command that combines phosphor build with Python's built-in HTTP server. Port numbers outside the valid range (1-65535) are rejected with a clear error. If the port is already in use, Phosphor prints a helpful message instead of crashing.
The serve command uses Python's http.server module, which is designed for development only. For production, build with phosphor build and deploy the _site/ directory to a proper web server or static hosting service.
Architecture
How Phosphor Works
Phosphor is a build-time static site generator. There's no runtime server, no client-side rendering, and no JavaScript framework. The build process transforms Markdown files into complete HTML pages.
Build Pipeline
Reads docs.yaml, merges with defaults. Validates that all required fields exist and all referenced page files are present.
Processes each .md file in two passes: first extracts ::: component blocks and converts them to HTML, then processes standard Markdown (headings, paragraphs, lists, code, tables).
Substitutes variables ({{TITLE}}, {{NAV}}, {{CONTENT}}) in the base HTML template. Builds sidebar navigation HTML from the config.
Walks all parsed pages, extracts every heading and surrounding text, generates a JSON search index, and injects it into the search JavaScript file.
Copies theme assets to _site/assets/, writes each rendered HTML page to _site/, and produces the final search.js with the embedded index.
File Structure
The Parser
The Markdown parser works in two passes:
- Fenced block pass: Scans for
:::typeblocks and converts them into HTML. Supports nesting (e.g., cards inside a section). This pass runs first so component HTML doesn't get processed as Markdown.
- Standard Markdown pass: Processes the remaining text for headings, paragraphs, lists, code blocks, tables, bold, italic, links, and images. HTML blocks from the first pass are passed through untouched.
The Template
The base HTML template at templates/base.html defines the page shell:
| Placeholder | Content |
|---|---|
{{TITLE}} | Site title (from site.title) |
{{SITE_TITLE}} | Same as TITLE (sidebar header) |
{{TAGLINE}} | Subtitle (from site.tagline) |
{{LOGO_TEXT}} | 1-2 char logo text |
{{FAVICON}} | Favicon path |
{{NAV}} | Generated sidebar navigation HTML |
{{GITHUB_LINK}} | GitHub link HTML (or empty) |
{{CONTENT}} | Parsed page content HTML |
Dependencies
| Dependency | Version | Purpose |
|---|---|---|
| Python 3 | 3.8+ | Runtime |
| PyYAML | 6.0 - 6.x | docs.yaml parsing |
| Lucide Icons | CDN (latest) | Icon library (loaded at runtime from CDN) |
| Google Fonts | CDN | Chakra Petch, Nunito Sans, JetBrains Mono |
No Node.js, no npm, no build tools. The JavaScript and CSS are hand-written static files.
Deployment
Static Hosting
The _site/ directory contains pure static HTML, CSS, and JavaScript. It can be served by any web server or static hosting service.
GitHub Pages
Or with an existing repo, push the _site/ contents to a gh-pages branch.
Netlify / Vercel / Cloudflare Pages
Point the hosting service to your repo and set:
- Build command:
cd path/to/docs && phosphor build(or just copy_site/) - Publish directory:
_site/
Nginx
server {
listen 80;
server_name docs.example.com;
root /var/www/docs;
index index.html;
location / {
try_files $uri $uri/ =404;
}
}
Apache
DocumentRoot "/var/www/docs"
DirectoryIndex index.html
Rsync
For simple deployment to any server:
Troubleshooting
command not found: phosphor
The phosphor command isn't in your PATH. Either:
- Run the installer:
./install.sh - Add
~/.local/binto your PATH:export PATH="$HOME/.local/bin:$PATH" - Run directly:
python3 -m phosphor.cli build
Config not found: docs.yaml
You're running phosphor build from a directory that doesn't contain docs.yaml. Either:
cdto your docs project directory first- Pass the directory as an argument:
phosphor build path/to/docs/ - Run
phosphor initto create a new docs project
Warning: Page not found
A file listed in the pages array of docs.yaml doesn't exist in the pages/ directory. Check:
- The filename spelling matches exactly (case-sensitive)
- The file is in the
pages/subdirectory, not the project root - The file has the
.mdextension
ModuleNotFoundError: No module named 'yaml'
PyYAML isn't installed. Fix with: pip install pyyaml
If you have multiple Python versions, make sure you're installing for the right one: python3 -m pip install pyyaml
Build output looks broken (no styling)
The CSS isn't loading. Check:
- The
_site/assets/directory containsstyle.css,script.js, andsearch.js - You're opening the HTML files through a web server (use
phosphor serve), not directly asfile://URLs. Some browsers block local file loading. - The
theme/directory in your Phosphor installation has all the asset files
Search returns no results
The search index might be empty. Verify:
- Your pages have
##headings (h2) — these are the primary units for search indexing - The pages are listed in the
pagesarray indocs.yaml - Rebuild with
phosphor buildafter adding new content
Slow build on WSL2
If your docs project is on the Windows filesystem (/mnt/c/...), file operations are slow due to the WSL2 filesystem bridge. Move your project to the Linux filesystem (~/my-docs/) for faster builds.
Error: port must be between 1 and 65535
The port number passed to phosphor serve -p is out of range. Use a port between 1 and 65535. Common choices: 3000, 5000, 8000, 8080.
Error: port is already in use
Another process is using that port. Either stop the other process or use a different port: phosphor serve -p 3001
Error: page path escapes pages/ directory
A file in the pages list references a path outside the pages/ directory (e.g., ../../../etc/passwd). Page paths must be simple filenames within the pages/ directory. Remove any ../ or absolute paths from the pages list.
Error: favicon path escapes project directory
The favicon value in docs.yaml points outside the project directory. The favicon path must be relative and resolve within the project root. Remove any ../ sequences that escape the project.
Lucide icons not rendering
Icons appear as empty squares or text. This means the Lucide CDN script isn't loading. Check:
- You have internet connectivity (Lucide loads from
unpkg.com) - Your browser isn't blocking CDN scripts
- The generated HTML includes the Lucide script tag near the bottom
FAQ
What's the minimum I need to get started?
Python 3.8+, PyYAML, and the Phosphor repo. Run phosphor init, edit the generated files, run phosphor build. That's it.
Can I use this for any project?
Yes. Phosphor is project-agnostic. The theme and components work for CLI tools, libraries, APIs, internal tools, or any technical documentation. Just change the config and write your content.
How many pages can Phosphor handle?
As many as you need. Phosphor processes pages sequentially and the build is fast (sub-second for typical doc sites). Each page is an independent HTML file.
Can I have multiple docs sites?
Yes. Each docs site is an independent directory with its own docs.yaml and pages/. They all share the same Phosphor installation (theme, templates, CLI). You can have dozens of sites.
Does it support syntax highlighting?
Code blocks use the Phosphor monospace styling but don't include language-specific syntax highlighting. For terminal blocks specifically, Phosphor colors commands, output, and comments differently. General-purpose syntax highlighting could be added via a post-processing step.
Can I customize the HTML template?
Yes. Edit templates/base.html in the Phosphor installation to change the page structure, add analytics scripts, modify the footer, or add custom meta tags. The template uses {{VAR}} placeholders.
Is JavaScript required?
The site works without JavaScript for basic reading. JavaScript enhances the experience with: search, table of contents generation, scroll spy navigation highlighting, and Lucide icon rendering. All content is readable without JS.
How do I update Phosphor?
Pull the latest changes from the repository. Your project files (docs.yaml, pages/) are separate from the Phosphor installation, so updates won't affect your content.
Can I contribute or fork?
Yes. Phosphor is open source. Fork the repo, modify the theme, add components, or adapt it for your needs. The codebase is intentionally small and readable.