From e052c8326f589418f2f1563ff51236d038aa8057 Mon Sep 17 00:00:00 2001 From: saberzero1 Date: Sat, 28 Feb 2026 04:33:34 +0100 Subject: [PATCH] docs: add PageFrame system to architecture overview --- ARCHITECTURE_OVERVIEW.md | 151 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 147 insertions(+), 4 deletions(-) diff --git a/ARCHITECTURE_OVERVIEW.md b/ARCHITECTURE_OVERVIEW.md index cd6177abd..b590fed83 100644 --- a/ARCHITECTURE_OVERVIEW.md +++ b/ARCHITECTURE_OVERVIEW.md @@ -26,6 +26,7 @@ This document provides a bird's-eye view of the Quartz v5 rework for maintainer - [QuartzComponent](#quartzcomponent) - [Component Registry](#component-registry) - [Page Rendering](#page-rendering) + - [Page Frames](#page-frames) - [Transclusion](#transclusion) - [Configuration](#configuration) - [quartz.config.yaml](#quartzconfigyaml) @@ -304,6 +305,7 @@ interface QuartzPageTypePluginInstance { match: PageMatcher // Determines if this page type handles a given page generate?: PageGenerator // Produces virtual pages (tag listings, folder pages, etc.) layout: string // Layout key (used for per-page-type overrides) + frame?: string // PageFrame name (e.g. "full-width", "minimal") — defaults to "default" body: QuartzComponentConstructor // The Body component for rendering page content } ``` @@ -642,9 +644,149 @@ The layout builder looks up components by: 1. Deep-clones the hast tree (to preserve the cached version) 2. Resolves transclusions (see below) -3. Destructures the layout into Head, header[], beforeBody[], Content, afterBody[], left[], right[], Footer -4. Renders the full HTML document as a Preact JSX tree -5. Serializes to HTML string via `preact-render-to-string` +3. Destructures the layout into Head, header[], beforeBody[], Content, afterBody[], left[], right[], Footer, and frame name +4. Resolves the frame via `resolveFrame(frameName)` — falls back to `DefaultFrame` if unset +5. Delegates the inner page structure to `frame.render({...all slots...})` +6. Wraps the frame output in the stable outer shell: `` → `` → `` → `
` → `` +7. Serializes to HTML string via `preact-render-to-string` + +The `data-frame` attribute on `#quartz-root` enables CSS targeting per-frame (see [Page Frames](#page-frames)). +### Page Frames + +The PageFrame system allows page types to optionally define completely different inner HTML structures (e.g. with/without sidebars, horizontal layouts, minimal chrome) while the outer shell remains stable for SPA navigation. + +#### Architecture + +``` +┌──────────────────────────────────────────────────┐ +│ │ Stable outer shell +│ │ (never changes) +│ │ +│
│ +│ │ +│ ┌──────────────────────────────────┐ │ +│ │ PageFrame.render() │ │ ← Frame controls this +│ │ (sidebars, header, content, etc.)│ │ +│ └──────────────────────────────────┘ │ +│ │ +│
│ +│ │ +│ │ +└──────────────────────────────────────────────────┘ +``` + +#### Interfaces + +```typescript +interface PageFrame { + name: string // e.g. "default", "full-width", "minimal" + render: (props: PageFrameProps) => JSX.Element // Renders the inner page structure + css?: string // Optional frame-specific CSS +} + +interface PageFrameProps { + componentData: QuartzComponentProps // Shared props for all components + head: QuartzComponent // Head component (for completeness) + header: QuartzComponent[] // Header slot + beforeBody: QuartzComponent[] // Before-body slot + pageBody: QuartzComponent // Content component (from PageType) + afterBody: QuartzComponent[] // After-body slot + left: QuartzComponent[] // Left sidebar components + right: QuartzComponent[] // Right sidebar components + footer: QuartzComponent // Footer component +} +``` + +#### Built-in Frames + +| Frame | Name | Description | Used By | +| --- | --- | --- | --- | +| **DefaultFrame** | `"default"` | Original three-column layout: left sidebar + center (header, beforeBody, content, afterBody) + right sidebar + footer | All page types by default | +| **FullWidthFrame** | `"full-width"` | No sidebars. Single center column spanning full width with header, content, afterBody, and footer | `canvas-page` | +| **MinimalFrame** | `"minimal"` | No sidebars, no header/beforeBody chrome. Only content + footer | `404` page | + +#### Frame Resolution Priority + +Frames are resolved in the `PageTypeDispatcher.resolveLayout()` function with this priority chain: + +``` +1. YAML config override: layout.byPageType..template +2. Page type declaration: pageType.frame (in plugin source) +3. Default: "default" +``` + +This means site authors can override any page type's frame via configuration without touching plugin code: + +```yaml +layout: + byPageType: + canvas: + template: minimal # Override canvas pages to use minimal frame +``` + +#### Creating Custom Frames + +New frames are added in `quartz/components/frames/`: + +1. Create a new `.tsx` file implementing the `PageFrame` interface +2. Register it in `quartz/components/frames/index.ts` → `builtinFrames` registry +3. Reference it by name in page type plugins or YAML config + +```typescript +// quartz/components/frames/PresentationFrame.tsx +import { PageFrame, PageFrameProps } from "./types" + +export const PresentationFrame: PageFrame = { + name: "presentation", + render({ componentData, pageBody: Content, footer: Footer }: PageFrameProps) { + return ( + <> +
+ +
+