- Status: Accepted
- Date: 2026-07-14
- Deciders: thulasi.ram (architect)
- Builds on: 0028 (SolidJS UI,
ui/), 0012 (REST/OpenAPI —openapi.jsonis the reference source), 0016 (docs-as-code, one repo) - Consumes:
docs/adr/**,CONTEXT.md,openapi.json,docs/DESIGN.md(the design system)
Context
Section titled “Context”Scarab is a tool, and a tool needs a published site. The forces:
- Content is lopsided toward design, today. The valuable, real artifacts that exist now are
the 41 ADRs +
CONTEXT.md(the “why”). The artifacts a normal doc site sells — install, tutorials, pipeline authoring, CLI/API reference — are largely future content, because the product is “design + scaffolding” (README). A site must not document vaporware. - The chosen editorial stance is operator-primary. Lead with the adopter/operator (run it, evaluate it, configure it); keep the design rigor available but subordinate in a “Tech” section. So the IA is B-shaped even though today’s content is C-heavy — pages get published only when backed by shipped reality.
- We already own the two strongest inputs.
openapi.json(ADR-0012) makes an API reference free and always-correct; the local dev harness (just up/just demo,dev/) makes a “Run locally” quickstart true today. These are the tent-pole real pages. - The stack is Vite/JS already.
ui/is SolidJS on Vite. A React-and-webpack doc framework would import a second, heavier toolchain for capabilities (versioning, i18n, blog) we do not need yet. - We have a real design system.
docs/DESIGN.mddefines the Scarab identity (carapace-green surface ladder, copper outline-only, Inter + JetBrains Mono, zero shadows, rough.js’d Lucide doodles as “the soul”). The site must wear it — screenshots of the product must not look out of place on the page they sit on.
Decision
Section titled “Decision”-
Tool = Astro Starlight. Build the site with Astro Starlight, not Docusaurus. Starlight is Vite-native (matches
ui/), ships zero-config offline search (Pagefind), renders the OpenAPI reference viastarlight-openapi, and is light to run. Docusaurus’s headline wins (doc versioning, i18n, blog, plugin marketplace) are things we do not need yet; versioning is deferred to a later add (“a version widget”, not a v1 feature). mdBook is rejected — it is a book, weak at the landing/screenshot and OpenAPI jobs we require. -
In-repo;
ui/becomes a container. The site lives in this repo so it can source ADRs andopenapi.jsonin place with no cross-repo sync.ui/is promoted to a container (likecrates/) holding independent front-end projects:crates/ # Rust workspace members (unchanged)docs/ # canonical source: ADRs, handoffs, DESIGN.md (UNCHANGED)ui/scarab-web-ui/ # the SolidJS app (moved from ui/)scarab-docs-ui/ # the Starlight site (new)docs/is not renamed — it is the source of truth (41 ADRs cross-link by relativedocs/adr/NNNNpaths; renaming it is expensive and pointless).scarab-docs-ui/is a self-contained Astro project sharing nothing withscarab-web-ui/(ownpackage.json, own build). -
Single source of truth — read in place, never copy. The site renders
docs/adr/**via an Astro content-collection glob (or symlink) and the API reference from../../openapi.jsonviastarlight-openapi, both at build time. No duplicated markdown; ADRs stay authored indocs/. -
IA is operator-primary, published honestly. Sidebar: Home (landing: the durability wedge, screenshots) · Get Started (Run locally — real today; Deploy with Helm — stub) · Guides (pipeline authoring; grows as slices land) · Configure (ENV/config reference) · Reference (API, auto-generated) · Tech (
CONTEXT.md+ ADRs, sourced in place). Pages exist in the nav only when backed by shipped reality; aspirational pages are clearly-marked stubs, never a broken headline. -
Hosting = GitHub Pages, release-gated. Build in a
.github/workflows/Action and publish to GitHub Pages at subpathbase: '/scarab-ci/'. Trigger onpush: tags: 'v*'+workflow_dispatch— tags are the production path once releasing; the manual dispatch is the escape hatch to publish on demand during the pre-tag build-out. (No auto-publish on everymaincommit.) -
Branding =
docs/DESIGN.md, tuned for documentation. Port the palette onto Starlight’s--sl-color-*tokens (carapace ladder backgrounds,--emeraldaccent,--copperoutline-only), Inter + JetBrains Mono, zero shadows. Tuned for docs: OS-respecting light + dark (both mapped to the palette; screenshots hairline-framed in light mode so a dark shot never floats), looser reading density than the dense control plane, screenshots styled as hairline-bordered cards. Doodles run throughout (the identity), honoring DESIGN.md’s guardrails (background layer, 5–10% opacity,pointer-events: none, ≤2 per page). The rough.js pass is a separate offline generator, not a runtime or per-build step: a standalone script reads source (Lucide) SVGs → runs them through rough.js once → writes the rough’d SVGs to a committed assets directory. The site consumes those static SVGs and never depends on rough.js (mirrors the existingopenapi-typescriptgenpattern — regenerate on demand, commit the output). Motion is best-effort and operates on the static SVGs: parallax drift + SVGstroke-dashoffsetdraw-on, gated behindprefers-reduced-motion; no Lottie (it means hand-authored animation + a runtime lib, violating DESIGN.md §8 “Lucide + rough.js, nothing else”). If motion gets fiddly, ship static doodles — nothing structural is lost.
Consequences
Section titled “Consequences”- + The site is real from day one despite a pre-product state: ADRs +
CONTEXT.md(Tech), the OpenAPI reference, and a working local quickstart are all true content, not placeholders. - + Zero content drift. ADRs and the API reference render from their canonical files in place; a PR that adds an ADR or changes the API updates the site in the same commit.
- + Toolchain coherence. A second front-end project, but same Vite family as
ui/; no React/webpack toolchain added. - −
ui/reorg has a blast radius (mechanical): the deeper nesting shifts relative paths —scarab-web-ui’s gen script../openapi.json→../../openapi.json;Justfilenpm --prefix ui→ui/scarab-web-ui;.gitignore→ui/**/node_modules,ui/**/dist; prose refs inDESIGN.md/handoffs/ADR-0028. Done as its own commit before scaffolding the site. - ± The doodle system is a decoupled generator, not site-build coupling. DESIGN.md’s SolidJS runtime component is not reused; instead a standalone script rough.js-processes source SVGs once and commits the output. This removes rough.js from the site’s runtime and build entirely — the site just serves static SVGs — so the only real work is the one-time generator + a small client motion script; the framework-port risk goes away.
- − Release-gated publishing means the site stays dark until the first
v*tag (mitigated byworkflow_dispatch). Between tags the published Tech section lagsmain— acceptable, and arguably desirable (published = a coherent snapshot). - Follow-ups (out of scope here): authoring + distributing the Helm chart (the “Deploy with
Helm” page is a stub until it exists); real screenshots (once
scarab-web-ui+ server render something worth showing); doc versioning (the deferred “widget”).
Alternatives considered
Section titled “Alternatives considered”- Docusaurus — the user’s initial lean. Rejected: React + its own webpack build is heavier and off-stack; its differentiators (versioning, i18n, blog, plugin marketplace) are all deferred needs. Starlight covers everything we need now at a fraction of the weight, and stays Vite-native.
- mdBook (Rust-idiomatic) — rejected: strong for a book, but weak at the landing/screenshot job we explicitly want and has no first-class OpenAPI story.
- Separate docs repo — rejected: would force syncing
docs/adr/**andopenapi.jsonacross repos (submodule/copy/publish-artifact), pure overhead for a solo pre-1.0 project, and it rots. In-repo keeps a single source of truth. - Rename
docs/→ free up “docs” for the site — rejected:docs/is the canonical source with 41 ADRs cross-linked by relative path; the rename is expensive and buys nothing. The-uisuffix onscarab-docs-uidisambiguates “the site app” from “docs/the content” instead. - Lottie / hand-authored animated doodles — rejected: violates DESIGN.md §8 (doodles are Lucide→rough.js, never hand-authored) and adds a runtime animation lib. The on-brand motion (parallax + rough.js draw-on) is cheaper and truer to “a little alive”.