tosijs-ui/site — static, pre-rendered, hydrating doc sites

A build system that turns a project's markdown (.md files + /*# block comments in source) into a fast, SEO/AI-friendly documentation site that works with no JavaScript and then upgrades itself into the interactive <tosi-doc-system> doc browser when the bundle loads.

The output is a plain folder of static files — drop it on GitHub Pages, Firebase Hosting, or any static host.

Status: extraction in progress. The runtime component lives here in src/doc-system/; the build tooling still lives in bin/ (site-config.ts, generate-site.ts, generate-css.ts, generate-og.ts, build-dom-shim.ts, docs.ts) and is being consolidated into src/doc-system/ behind the importable tosijs-ui/site entry described here. See "Current layout" at the bottom.

What you get

How it works (pipeline)

extractDocs(docPaths)            →  docs.json   (markdown corpus)
generateSite(config, docs)       →  /{slug}/index.html + docs.json + sitemap + robots
generate-css(theme)              →  doc-system.css   (burned-in, no FOUC)
bundle(bundleEntry | iife.js)    →  the JS that hydrates the pages
host preset                      →  .nojekyll / CNAME / firebase.json

Static and hydrated output share the same slug + markdown rendering (src/doc-system/routing + render) so the page never reflows on hydration.

Quick start (adopting in your project)

1. site.config.ts at your repo root:

import { defineSiteConfig } from 'tosijs-ui/site'

export default defineSiteConfig({
  name: 'my-lib',
  description: 'What my library does.',
  baseUrl: 'https://my-lib.example.com',
  host: 'github-pages',          // emits .nojekyll + CNAME (domain from baseUrl)
  bundleEntry: 'demo/site.ts',   // omit to use tosijs-ui's published iife.js
  navbarLinks: [
    { href: 'https://github.com/me/my-lib', label: 'github', icon: 'github' },
  ],
})

2. bin/site.ts — the only build file you write:

import { buildSite, devServer } from 'tosijs-ui/site'
import config from '../site.config'

process.argv.includes('--build') ? buildSite(config) : devServer(config)

3. scripts in package.json:

{ "scripts": { "start": "bun bin/site.ts", "build": "bun bin/site.ts --build" } }

Bundles & live examples (read this)

The static pages are inert HTML until a JS bundle loads and registers the custom elements (and powers live js/test examples). You pick one of two modes:

Configuration reference

All fields are optional except name. See bin/site-config.ts for the authoritative typed definition.

Identity & SEO

field default purpose
name brand name; <title> suffix, og:site_name
description site-level meta + structured-data fallback
baseUrl absolute origin for canonical/OG/sitemap URLs
lang 'en' <html lang>
favicon /favicon.svg favicon href
ogImage default share image (per-page overridable)
headExtra raw lines injected into every <head>

Branding & chrome

field default purpose
projectLinks logo + view-source links
navbarLinks header-bar icon links
theme base colors (palette derived from accent)
localizedStrings TSV table for the language picker

Doc sources

field default purpose
docPaths ['src', 'README.md'] dirs scanned for /*# + .md files (list root .md files explicitly)

Bundle

field default purpose
bundleEntry your IIFE entrypoint; omit to use the fallback bundle
bundleExternals modules left external, e.g. ['jolt-physics']
scriptUrl /iife.js bundle URL pages load (fallback + output name)

Static assets

field default purpose
staticDirs ['demo/static'] or ['static'] dirs copied to the web root

Hosting

field default purpose
host 'static' 'github-pages' | 'firebase' | 'static' preset
domain derived from baseUrl custom domain → CNAME (github-pages); implies basePath: '/'
basePath '/' URL prefix; set '/<repo>' for a GitHub project page without a custom domain

Build toggles & dev server

field default purpose
generateIcons false run icon-data generation (tosijs-ui-specific)
llmsTxt true emit llms.txt discoverability index
outputDir 'docs' served web-root output dir
port 8787 dev-server port
watchPaths extra dev-server watch dirs

Host presets & custom domains

host .nojekyll CNAME basePath other
github-pages + domain domain /
github-pages, no domain set '/<repo>' yourself
firebase / optional firebase.json rewrites
static (default) / nothing host-specific

domain is derived from baseUrl's hostname when omitted (and host: 'github-pages'), so the common case needs no extra config; set it explicitly to override (apex vs www, or a domain that differs from the canonical origin). A custom domain always serves from root, so it forces basePath: '/'.

Doc format

Notes & gotchas

Current layout (extraction in progress)

concern today target
config type + defineSiteConfig bin/site-config.ts src/site/
orchestrator (prebuild/build/serve) bin/dev.ts src/site/ (buildSite/devServer)
static page generator bin/generate-site.ts src/site/
theme → static CSS bin/generate-css.ts src/site/
OG image generation bin/generate-og.ts src/site/
doc extraction bin/docs.ts src/site/
runtime component src/doc-system/ unchanged (ships in the bundle)