Skip to content

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#

CrateRole
qquill-viewView 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-styleCSS 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-themeTyped 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-iconsSVG 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-signalServer-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-uiHeadless 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-designStyled 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-docsReusable 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-runtimeThe 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-buildBuild 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-cliThe `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)
Internal dependency shape (arrows point at the dependency)

Read straight off the crates' Cargo.toml files, the exact [dependencies] are:

CrateDepends on (Quill crates only)
qquill-view(nothing)
qquill-style(nothing)
qquill-theme(nothing)
qquill-runtime(nothing)
qquill-build(nothing)
qquill-cli(nothing)
qquill-iconsqquill-view
qquill-signalqquill-view
qquill-uiqquill-view
qquill-designqquill-view, qquill-style, qquill-theme, qquill-ui, qquill-icons
qquill-docsqquill-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 std and 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 quill CLI. Self-contained and std-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:

toml
[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.