Quill docs
Project structure & architecture
Quill is not a single crate — it is a small workspace of focused, layered crates, each std-only and each depending only on other Quill crates. This page maps that workspace: what every crate does, how they depend on one another (always inward, never the reverse), how Quill relates to the rest of Qirava, and what the binary a quill new app produces actually pulls in. The organizing rule is simple and strict: zero third-party dependencies anywhere, and any JavaScript that ships is hand-written.
The big picture#
Quill is a product (a sibling to the Qirava DMS), not part of the shared q* stdlib. It lives in its own qquill/ repo and is consumed by products — the DMS's Studio admin app and the docs site — via path dependencies. Two invariants hold across the whole workspace: every Quill crate is std-only and depends only on other Quill crates (there is no dependency on the q*/qpkgs stdlib from within Quill, and q* never depends on Quill); and the client runtime is hand-written vanilla JavaScript with zero runtime dependencies.
The crates#
| Crate | Role |
|---|---|
| qquill-view | View core: a borrow-friendly `Node` tree (Element/Text/Raw/Fragment + Island/Slot), the `view!{}` authoring macro, a builder API, and a streaming HTML renderer with a hand-written escaper. The root crate — everything depends on it, it depends on nothing. |
| qquill-style | CSS compiler (qcss successor): turns one co-located style block (the `style!{}` macro or builder API) into deterministic, escaped CSS text for inlining into the SSR `<head>`. |
| qquill-theme | Typed design-token system (qtheme successor): tokens emitted as `--q-*` CSS custom properties; light/dark/contrast modes as token overrides; typed Rust `token()` accessors; a tiny no-flicker pre-paint boot script. |
| qquill-icons | SVG icon system over the view tree: const path data, an `Icon` enum for compile-time name validation, `icon() -> Node` inline SVG with ARIA/sizing, and the Qirava brand logos as const data. |
| qquill-signal | Server-inert / client-reactive `Signal<T>`: on the server it holds an initial value, renders it into inert HTML via a `data-q-bind` marker, and serializes it into island props. The matching client behavior lives in qquill-runtime. |
| qquill-ui | Headless component state machines (button, field, checkbox, toggle, tabs, disclosure, dialog, menu, tooltip, and more): pure State + transitions + an `attrs()` producing the correct ARIA/data attributes onto a view `Node`. |
| qquill-design | Styled components (qdesign successor): maps qquill-ui headless state machines onto view Nodes with classes and theme vars, returning both the Node and a companion style block. Variants are const lookup tables, not runtime string-building. |
| qquill-docs | Reusable documentation-site components built on the full stack: `DocPage`, a `DocShell` layout (brand header + theme toggle + sidebar + content), `CodeBlock`/`CodeTabs`, `Callout`, headings/anchors, API entries, prev/next nav. |
| qquill-runtime | The hand-written client islands runtime: a batched signal core (signal/computed/effect), lazy hydration triggers, the island hydrator implementing the props-sidecar contract, and a behavior registry keyed by `data-q-kind`. Rust `include_str!`s the JS and concatenates it into one minified IIFE. |
| qquill-build | Build helpers: folder-route codegen (route path → handler key → `_sys_routes` row), the SSG prerender driver, and the static-export writer (URL→file mapping, `public/` copy, Cloudflare Pages `_headers`/`_redirects`/`404.html`). |
| qquill-cli | The `quill` scaffolder binary: `quill new <name>` writes a starter app; `quill build` drives a static export. |
The dependency graph#
Products and higher-level crates depend inward — toward qquill-view — and never the reverse. qquill-view is the root and has no dependencies at all. qquill-runtime, qquill-build, and qquill-style are standalone (no Quill deps of their own beyond what they compose), while qquill-design and qquill-docs sit at the top, composing the rest.
qquill-view ── (root; no deps)
▲ ▲ ▲
icons ──┘ │ └── signal, ui ──┐
style ─────┤ │
theme ─────┤ │
▼ ▼
qquill-design ──────► qquill-docs
qquill-runtime, qquill-build (standalone)Read straight off the crates' Cargo.toml files, the exact [dependencies] are:
| Crate | Depends on (Quill crates only) |
|---|---|
| qquill-view | (nothing) |
| qquill-style | (nothing) |
| qquill-theme | (nothing) |
| qquill-runtime | (nothing) |
| qquill-build | (nothing) |
| qquill-cli | (nothing) |
| qquill-icons | qquill-view |
| qquill-signal | qquill-view |
| qquill-ui | qquill-view |
| qquill-design | qquill-view, qquill-style, qquill-theme, qquill-ui, qquill-icons |
| qquill-docs | qquill-view, qquill-style, qquill-theme, qquill-icons, qquill-ui, qquill-design |
Zero third-party dependencies#
"Zero third-party deps" is load-bearing, not a slogan. Each crate is std-only: the HTML escaper in qquill-view is hand-written, the CSS compiler in qquill-style emits its own escaped text, and the client runtime in qquill-runtime is hand-authored vanilla JavaScript that Rust embeds with include_str! and concatenates into a single minified IIFE. Node appears only as a dev/test tool and is never shipped.
- No supply-chain surface beyond
stdand your own Qirava checkout. - Deterministic output: attribute order is preserved exactly as inserted, so renders are reproducible.
- A build-twice run yields byte-identical HTML and identical strong (content-hash) ETags — the SSG/export determinism gate.
- The whole framework is auditable end to end, including the JavaScript that reaches the browser.
How Quill fits into Qirava#
- qpkgs/
- The shared
q*stdlib (crypto, encoding, value model, executor, …). Quill does not use it. Products that need both (like the DMS) depend on each separately. - qdms/
- The DMS product. It consumes Quill — for its Studio admin app and its docs site — via
../qquill/qquill-*path dependencies, and provides the worker host that serves a Quill app's routes. - qquill/
- This repo: the Quill framework crates plus the
quillCLI. Self-contained andstd-only.
The hard invariants for the whole workspace ("Packages vs products") live in the top-level AGENTS.md. The short version: q* packages never depend on products; products may depend on packages and on Quill; Quill depends on neither qpkgs nor the DMS.
What a generated app pulls in#
A quill new app is itself a plain Rust binary, and its dependency set makes the architecture concrete. Its Cargo.toml lists path dependencies on the DMS engine and a subset of Quill crates — and nothing third-party:
[dependencies]
qdms = { path = "<QIRAVA_ROOT>/qdms" }
qexec = { path = "<QIRAVA_ROOT>/qpkgs/qexec" }
qvalue = { path = "<QIRAVA_ROOT>/qpkgs/qvalue" }
qquill-view = { path = "<QIRAVA_ROOT>/qquill/qquill-view" }
qquill-style = { path = "<QIRAVA_ROOT>/qquill/qquill-style" }
qquill-build = { path = "<QIRAVA_ROOT>/qquill/qquill-build" }
qquill-runtime = { path = "<QIRAVA_ROOT>/qquill/qquill-runtime" }The app depends on the DMS (qdms) because at serve time it uses the DMS engine (Qdb) and worker host to actually serve the HTTP routes; qexec/qvalue are the engine's executor and value types. From Quill it pulls in qquill-view (to build pages), qquill-style (the theme), qquill-build (the static export), and qquill-runtime (the island bundle). Higher-level crates like qquill-ui, qquill-design, qquill-icons, and qquill-docs are not in the starter — you add them when you want their components.