From f1a4a8a48f69547da250b971efb43a11115c7537 Mon Sep 17 00:00:00 2001 From: Ahmed Yasin Koculu Date: Mon, 5 Oct 2026 22:37:13 +0200 Subject: [PATCH] Add new categories to the guides. --- README.md | 10 +- .../purestack.studio/components/_nav.json | 2 +- .../actions/alert-box/alert-box.mdx | 4 +- .../components/actions/btn-link/btn-link.mdx | 4 +- .../site/nav-menu/nav-menu-playground.ts | 4 +- frontend/purestack.studio/guides/_nav.json | 18 +-- .../purestack.studio/guides/content/_nav.json | 9 ++ .../guides/{ => content}/code-blocks.mdx | 2 +- .../purestack.studio/guides/content/index.mdx | 18 +++ .../guides/{ => content}/links.mdx | 8 +- .../guides/content/localization.mdx | 78 ++++++++++ .../guides/{ => content}/shared-content.mdx | 30 +++- .../guides/extending/_nav.json | 7 + .../guides/extending/index.mdx | 16 ++ .../guides/{ => extending}/plugins.mdx | 98 ++++-------- .../guides/extending/plugins/page-index.ts | 35 +++++ .../{ => extending}/regor-launch-checklist.ts | 0 .../guides/{ => extending}/regor.mdx | 139 +++--------------- .../guides/getting-started/_nav.json | 8 + .../guides/getting-started/index.mdx | 17 +++ .../{ => getting-started}/purestack-cli.mdx | 10 +- .../{ => getting-started}/site-config.mdx | 6 +- .../vscode-extension.mdx | 2 +- frontend/purestack.studio/guides/index.mdx | 75 +++------- .../guides/publishing/_nav.json | 7 + .../guides/publishing/deployment.mdx | 81 ++++++++++ .../guides/publishing/index.mdx | 16 ++ .../guides/publishing/search-and-metadata.mdx | 110 ++++++++++++++ .../purestack.studio/guides/styling/_nav.json | 10 ++ .../guides/{ => styling}/icons.mdx | 4 +- .../purestack.studio/guides/styling/index.mdx | 19 +++ .../guides/{ => styling}/semantic-tones.mdx | 0 .../semantic-tones/playground.ts | 0 .../guides/{ => styling}/themes.mdx | 2 +- .../guides/{ => styling}/typography.mdx | 4 +- .../{ => styling}/utility-css-classes.mdx | 6 +- frontend/purestack.studio/index.mdx | 6 +- packages/purestack/README.md | 10 +- packages/ts-html/README.md | 10 +- packages/ts-ssg/README.md | 4 +- 40 files changed, 577 insertions(+), 312 deletions(-) create mode 100644 frontend/purestack.studio/guides/content/_nav.json rename frontend/purestack.studio/guides/{ => content}/code-blocks.mdx (97%) create mode 100644 frontend/purestack.studio/guides/content/index.mdx rename frontend/purestack.studio/guides/{ => content}/links.mdx (89%) create mode 100644 frontend/purestack.studio/guides/content/localization.mdx rename frontend/purestack.studio/guides/{ => content}/shared-content.mdx (62%) create mode 100644 frontend/purestack.studio/guides/extending/_nav.json create mode 100644 frontend/purestack.studio/guides/extending/index.mdx rename frontend/purestack.studio/guides/{ => extending}/plugins.mdx (84%) create mode 100644 frontend/purestack.studio/guides/extending/plugins/page-index.ts rename frontend/purestack.studio/guides/{ => extending}/regor-launch-checklist.ts (100%) rename frontend/purestack.studio/guides/{ => extending}/regor.mdx (83%) create mode 100644 frontend/purestack.studio/guides/getting-started/_nav.json create mode 100644 frontend/purestack.studio/guides/getting-started/index.mdx rename frontend/purestack.studio/guides/{ => getting-started}/purestack-cli.mdx (92%) rename frontend/purestack.studio/guides/{ => getting-started}/site-config.mdx (98%) rename frontend/purestack.studio/guides/{ => getting-started}/vscode-extension.mdx (98%) create mode 100644 frontend/purestack.studio/guides/publishing/_nav.json create mode 100644 frontend/purestack.studio/guides/publishing/deployment.mdx create mode 100644 frontend/purestack.studio/guides/publishing/index.mdx create mode 100644 frontend/purestack.studio/guides/publishing/search-and-metadata.mdx create mode 100644 frontend/purestack.studio/guides/styling/_nav.json rename frontend/purestack.studio/guides/{ => styling}/icons.mdx (98%) create mode 100644 frontend/purestack.studio/guides/styling/index.mdx rename frontend/purestack.studio/guides/{ => styling}/semantic-tones.mdx (100%) rename frontend/purestack.studio/guides/{ => styling}/semantic-tones/playground.ts (100%) rename frontend/purestack.studio/guides/{ => styling}/themes.mdx (96%) rename frontend/purestack.studio/guides/{ => styling}/typography.mdx (98%) rename frontend/purestack.studio/guides/{ => styling}/utility-css-classes.mdx (96%) diff --git a/README.md b/README.md index 4da3ea1c..815c4a37 100644 --- a/README.md +++ b/README.md @@ -45,7 +45,7 @@ stays in Markdown; components, style builders, and scripts live in TypeScript. The aim is a source model that remains understandable as the product grows. `.mdx` and `.rmdx` pages use Regor components and expressions. Plain `.md` -files work for prose pages. See the [Regor guide](https://purestack.studio/guides/regor/) +files work for prose pages. See the [Regor guide](https://purestack.studio/guides/extending/regor/) for the markup and browser app model. ## Get your first page running @@ -128,10 +128,10 @@ has a complete example. | I want to… | Start here | | :--- | :--- | -| Configure a site | [Site configuration](https://purestack.studio/guides/site-config/) | +| Configure a site | [Site configuration](https://purestack.studio/guides/getting-started/site-config/) | | Build a richer page | [Component catalog](https://purestack.studio/components/) | | Add browser behavior | [PageScript and RegorApp examples](packages/purestack/README.md) | -| Understand the CLI | [CLI guide](https://purestack.studio/guides/purestack-cli/) | +| Understand the CLI | [CLI guide](https://purestack.studio/guides/getting-started/purestack-cli/) | | Explore a complete project | [Sample content](packages/ts-ssg/sample-content) · [Website source](frontend) | | Work on PureStack itself | [Contribution guide](CONTRIBUTING.md) | @@ -146,8 +146,8 @@ has a complete example. | `PageScript` and `RegorApp` | Bundle TypeScript and add browser behavior where requested. | | Static assets | Copies files from the content directory into the site output. | -The [CLI guide](https://purestack.studio/guides/purestack-cli/) covers the -commands and options. [Site configuration](https://purestack.studio/guides/site-config/) +The [CLI guide](https://purestack.studio/guides/getting-started/purestack-cli/) covers the +commands and options. [Site configuration](https://purestack.studio/guides/getting-started/site-config/) covers navigation, themes, search, sitemap, localization, and output paths. diff --git a/frontend/purestack.studio/components/_nav.json b/frontend/purestack.studio/components/_nav.json index 4f91ece1..a85d3a36 100644 --- a/frontend/purestack.studio/components/_nav.json +++ b/frontend/purestack.studio/components/_nav.json @@ -1,6 +1,6 @@ { "pageLinks": true, - "sequence": ["index.mdx", "guides"], + "sequence": ["guides", "index.mdx"], "items": [ { "id": "guides", diff --git a/frontend/purestack.studio/components/actions/alert-box/alert-box.mdx b/frontend/purestack.studio/components/actions/alert-box/alert-box.mdx index e69ba7ec..d40cd231 100644 --- a/frontend/purestack.studio/components/actions/alert-box/alert-box.mdx +++ b/frontend/purestack.studio/components/actions/alert-box/alert-box.mdx @@ -18,14 +18,14 @@ nav: ## In context -

Explain what happened, why it matters, and what the reader can do next.

+

Explain what happened, why it matters, and what the reader can do next.

```html

Explain what happened, why it matters, and what the reader can do next.

``` diff --git a/frontend/purestack.studio/components/actions/btn-link/btn-link.mdx b/frontend/purestack.studio/components/actions/btn-link/btn-link.mdx index 690345f0..0dd427bd 100644 --- a/frontend/purestack.studio/components/actions/btn-link/btn-link.mdx +++ b/frontend/purestack.studio/components/actions/btn-link/btn-link.mdx @@ -20,7 +20,7 @@ Use BtnLink to navigate to a page, a section, or an external destination. For an ## In context -
START HERE

Build your first interface

Learn how static content and interactive components fit together.

Explore RegorBrowse components
+
START HERE

Build your first interface

Learn how static content and interactive components fit together.

Explore RegorBrowse components
```html @@ -28,7 +28,7 @@ Use BtnLink to navigate to a page, a section, or an external destination. For an

Build your first interface

Learn how static content and interactive components fit together.

-Explore Regor +Explore Regor Browse components ``` diff --git a/frontend/purestack.studio/components/site/nav-menu/nav-menu-playground.ts b/frontend/purestack.studio/components/site/nav-menu/nav-menu-playground.ts index bf7f7890..ce47a1f0 100644 --- a/frontend/purestack.studio/components/site/nav-menu/nav-menu-playground.ts +++ b/frontend/purestack.studio/components/site/nav-menu/nav-menu-playground.ts @@ -89,7 +89,7 @@ const TREES: Record = { title: 'Guides', icon: 'tabler:book', children: [ - { title: 'Themes', url: '/guides/themes/', icon: 'tabler:palette' }, + { title: 'Themes', url: '/guides/styling/themes/', icon: 'tabler:palette' }, { title: 'Components', icon: 'tabler:components', @@ -143,7 +143,7 @@ const TREES: Record = { const DEFAULTS = { treeName: 'docs' as TreeName, - currentUrl: '/guides/themes/', + currentUrl: '/guides/styling/themes/', showIcons: true, tone: 'neutral' as SemanticTone, variant: 'flat' as ComponentVariant, diff --git a/frontend/purestack.studio/guides/_nav.json b/frontend/purestack.studio/guides/_nav.json index e2ab5952..60832a01 100644 --- a/frontend/purestack.studio/guides/_nav.json +++ b/frontend/purestack.studio/guides/_nav.json @@ -3,19 +3,11 @@ "sequence": [ "index.mdx", "components", - "purestack-cli.mdx", - "site-config.mdx", - "links.mdx", - "code-blocks.mdx", - "shared-content.mdx", - "vscode-extension.mdx", - "regor.mdx", - "typography.mdx", - "utility-css-classes.mdx", - "semantic-tones.mdx", - "icons.mdx", - "themes.mdx", - "plugins.mdx" + "getting-started/", + "content/", + "styling/", + "extending/", + "publishing/" ], "items": [ { diff --git a/frontend/purestack.studio/guides/content/_nav.json b/frontend/purestack.studio/guides/content/_nav.json new file mode 100644 index 00000000..93c58dab --- /dev/null +++ b/frontend/purestack.studio/guides/content/_nav.json @@ -0,0 +1,9 @@ +{ + "sequence": [ + "index.mdx", + "links.mdx", + "code-blocks.mdx", + "shared-content.mdx", + "localization.mdx" + ] +} diff --git a/frontend/purestack.studio/guides/code-blocks.mdx b/frontend/purestack.studio/guides/content/code-blocks.mdx similarity index 97% rename from frontend/purestack.studio/guides/code-blocks.mdx rename to frontend/purestack.studio/guides/content/code-blocks.mdx index 131ad3f5..f503fad2 100644 --- a/frontend/purestack.studio/guides/code-blocks.mdx +++ b/frontend/purestack.studio/guides/content/code-blocks.mdx @@ -25,7 +25,7 @@ const answer = 42 ``` ```` -The language selects the highlighting. The [site configuration](./site-config#markdown-and-code-blocks) chooses the highlighter. +The language selects the highlighting. The [site configuration](../getting-started/site-config#markdown-and-code-blocks) chooses the highlighter. ## Show a file's code diff --git a/frontend/purestack.studio/guides/content/index.mdx b/frontend/purestack.studio/guides/content/index.mdx new file mode 100644 index 00000000..5b8e0194 --- /dev/null +++ b/frontend/purestack.studio/guides/content/index.mdx @@ -0,0 +1,18 @@ +--- +title: Content +description: Write links and code examples, share content, and organize translations. +template: doc +nav: + icon: tabler:files +layout: + showToc: true +--- + +# Content + +Write links and code examples, share content, and organize translations. + +- [Links](./links) +- [Code blocks](./code-blocks) +- [Shared content](./shared-content) +- [Localization](./localization) diff --git a/frontend/purestack.studio/guides/links.mdx b/frontend/purestack.studio/guides/content/links.mdx similarity index 89% rename from frontend/purestack.studio/guides/links.mdx rename to frontend/purestack.studio/guides/content/links.mdx index 6c3de9bd..52156a43 100644 --- a/frontend/purestack.studio/guides/links.mdx +++ b/frontend/purestack.studio/guides/content/links.mdx @@ -13,7 +13,7 @@ nav: # Links -Write a link the way you see your files. PureStack turns it into the right address and checks it every time the site builds, so a renamed or deleted page or image never leaves a broken link behind. +Write local links relative to the source file. PureStack resolves them to public URLs and fails the build when the target page or asset is missing. Root-relative public URLs, external destinations, and fragment IDs have different validation rules, described below. ## Link to another page @@ -53,7 +53,7 @@ Add `#` and the section name after the page: [Back to the top](#links) ``` -A section name is its heading in lowercase, with hyphens between the words. The heading "Create a skin" becomes `#create-a-skin`. Try it: [create a skin](./themes#create-a-skin). +A section name is its heading in lowercase, with hyphens between the words. The heading "Create a skin" becomes `#create-a-skin`. Try it: [create a skin](../styling/themes#create-a-skin). PureStack checks that the page exists, not the section, so double-check section names after you rename a heading. @@ -103,9 +103,9 @@ Anything that starts with `/` or a protocol is used as written: PureStack doesn't check these links, because the pages behind them aren't part of your content folder. -You can link your own pages this way too, as in `/guides/themes/`, but a relative link is the better habit: if that page moves, the build tells you. +You can link your own pages this way too, as in `/guides/styling/themes/`, but a relative link is the better habit: if that page moves, the build tells you. -If the site lives under a base path, such as `/docs`, PureStack adds it to every link for you. Set it once in the [site configuration](./site-config). +If the site lives under a base path, such as `/docs`, PureStack adds it to every link for you. Set it once in the [site configuration](../getting-started/site-config). ## When a link is broken diff --git a/frontend/purestack.studio/guides/content/localization.mdx b/frontend/purestack.studio/guides/content/localization.mdx new file mode 100644 index 00000000..f1e6260f --- /dev/null +++ b/frontend/purestack.studio/guides/content/localization.mdx @@ -0,0 +1,78 @@ +--- +title: Localization +description: Organize translated pages, choose locale URLs, and understand development language selection and production hosting requirements. +template: doc +nav: + icon: tabler:language +layout: + showToc: true +--- + +# Localization + +PureStack recognizes translations by matching routes inside configured locale folders. You provide translated content; the build assigns locale URLs and associates matching pages. For a static host, start with `prefix-all`, which gives every language its own public URL. + +## Create matching content trees + +```text +content/ + siteConfig.json + en/ + index.mdx + header.mdx + guides/ + install.mdx + de/ + index.mdx + header.mdx + guides/ + install.mdx +``` + +Merge this into `siteConfig.json`: + +```json +{ + "i18n": { + "enabled": true, + "defaultLocale": "en", + "locales": ["en", "de"], + "urlStrategy": "prefix-all" + } +} +``` + +Write each page's title, description, body, and navigation labels in its own language. A locale-level `header.mdx` or `footer.mdx` supplies translated shared sections to its descendants. Other files outside locale directories remain ordinary, nonlocalized content. + +With the example tree, visit `/en/guides/install/` and `/de/guides/install/`. Both output files live in their locale folders: `en/guides/install/index.html` and `de/guides/install/index.html`. + +## Understand how translations match + +PureStack removes the locale prefix and resolves the remaining route to form a translation key. `en/guides/install.mdx` and `de/guides/install.mdx` therefore belong together. Their titles can differ. Renaming the German file to `installation.mdx` gives it a different key; translation matching does not use the title. + +There is no automatic translation step. Create the corresponding file for every translation you want to publish. The association contains the translations that actually exist, not placeholder pages for missing ones. + +For navigation, configure roots using actual content paths, such as `en/guides` and `de/guides`, and put localized `_nav.json` files in those folders when using hybrid or custom mode. + +## Link within and between languages + +Relative source links follow the current content tree. From `de/guides/install.mdx`, a link to `../index.mdx` resolves to the German home page. Link to a specific locale explicitly when implementing a language switcher. See [translated links](./links#translated-sites) for root-relative resolution rules. + +The page context exposes `locale`, `locales`, and translation information for custom components. Built-in templates set the document language from the page locale. When translations have distinct absolute URLs, the head builder emits alternate-language links; configure `sitemap.baseUrl` to supply the public origin. + +## Choose a URL strategy + +| Strategy | Public URLs | Production requirement | +| --- | --- | --- | +| `prefix-all` (default) | `/en/guides/install/`, `/de/guides/install/` | Serve the generated locale directories. | +| `hidden` | Both languages use `/guides/install/` | Route requests to the appropriate locale directory on the host or server. | + +`hidden` still writes separate locale directories. It does not produce one language-neutral HTML file, and uploading the output to a plain static host does not implement language selection. + +The development server chooses a requested locale using the configured query parameter, then the locale cookie, then `Accept-Language`, then the default locale. The defaults are `queryParam: "lang"` and `cookieName: "ts-ssg.lang"`. For example, `?lang=de` selects a configured German locale during development. Design and verify equivalent routing and cache behavior on your production host before using hidden URLs. + +## Defaults and validation + +A nonempty `locales` list enables localization even when `enabled` is omitted. `defaultLocale` falls back to the first locale; set it explicitly so list reordering does not change the default. Locale identifiers accept letters, numbers, and hyphens. Keep folder spelling and configured identifiers consistent. + +Before publishing, open a nested route in every language, follow a relative link, check the generated document's `lang` attribute, and inspect canonical and alternate links. A development redirect or preference cookie is server behavior, not a file that `publish` can upload for you. diff --git a/frontend/purestack.studio/guides/shared-content.mdx b/frontend/purestack.studio/guides/content/shared-content.mdx similarity index 62% rename from frontend/purestack.studio/guides/shared-content.mdx rename to frontend/purestack.studio/guides/content/shared-content.mdx index 2a2dfc4d..0f8f73d3 100644 --- a/frontend/purestack.studio/guides/shared-content.mdx +++ b/frontend/purestack.studio/guides/content/shared-content.mdx @@ -45,11 +45,39 @@ The page shows the passage as if you had written it there. Markdown and componen - **They are not pages.** A file whose name, or the name of a folder above it, starts with `_` is never published on its own, such as `_install.mdx` or `_shared/beta-note.mdx`. - **They have no frontmatter.** The page that shows a passage sets its title, template, and layout. -- **Links work from the page.** A link inside a passage resolves from the page that shows it. When pages in different folders show the passage, start its links at the site root, such as `/guides/themes/`. +- **Links work from the page.** A link inside a passage resolves from the page that shows it. When pages in different folders show the passage, start its links at the site root, such as `/guides/styling/themes/`. - **They can import too.** A shared file can show another shared file, or a file's [code](./code-blocks), with paths relative to the shared file itself. - **Inside a component, markup rules apply.** `` works inside component markup too, such as a `TabPane`. There, the passage is read like anything else written inside a component: HTML, components, and code blocks work, but other Markdown, such as `**bold**`, lists, and `[links](./page)`, stays as written. Write passages meant for components in HTML. - **Edits show up.** While `purestack serve` runs, saving a shared file updates every page that shows it. +## Share a header and footer + +Headers and footers use directory inheritance rather than ``. Add `content/header.mdx` to supply the site's header: + +```mdx + +``` + +Add `content/footer.mdx` for its footer: + +```mdx + +``` + +A page uses the nearest header and the nearest footer found by walking up its source folders. For example, `content/guides/header.mdx` replaces the root header for pages under `guides`, while those pages can still inherit the root footer. The two lookups are independent. `.rmdx` is also supported; keep only one header and one footer per directory across these extensions. + +Links in these sections resolve from the header or footer file, unlike links in an imported passage, which resolve from the consuming page. Use a content-root script path when a shared section loads browser code, or set `PageScript.sourceRelPath` explicitly. Frontmatter `layout.showFooter: false` hides the footer on one page. Custom templates receive `headerHtml` and `footerHtml` and decide where to place them. + +## Choose a passage, component, or template + +| Reuse requirement | Use | +| --- | --- | +| The same prose on several pages | An underscore-prefixed file and `import-content` | +| A shared header or footer for a section | `header.mdx` / `footer.mdx` in its directory | +| Markup with inputs or slots | A [Regor component](../extending/regor#write-build-time-components) registered by a plugin | +| A different document shell | A [page template](../extending/plugins#add-page-templates) | +| Source code shown verbatim | [`import-codeblock`](./code-blocks) | + ## When something is wrong The build stops and names the page and the tag: diff --git a/frontend/purestack.studio/guides/extending/_nav.json b/frontend/purestack.studio/guides/extending/_nav.json new file mode 100644 index 00000000..56e658fb --- /dev/null +++ b/frontend/purestack.studio/guides/extending/_nav.json @@ -0,0 +1,7 @@ +{ + "sequence": [ + "index.mdx", + "regor.mdx", + "plugins.mdx" + ] +} diff --git a/frontend/purestack.studio/guides/extending/index.mdx b/frontend/purestack.studio/guides/extending/index.mdx new file mode 100644 index 00000000..40fb567a --- /dev/null +++ b/frontend/purestack.studio/guides/extending/index.mdx @@ -0,0 +1,16 @@ +--- +title: Extending PureStack +description: Add browser behavior with Regor and extend the build with plugins. +template: doc +nav: + icon: tabler:plug +layout: + showToc: true +--- + +# Extending PureStack + +Add browser behavior with Regor and extend the build with plugins. + +- [Regor](./regor) +- [Plugins](./plugins) diff --git a/frontend/purestack.studio/guides/plugins.mdx b/frontend/purestack.studio/guides/extending/plugins.mdx similarity index 84% rename from frontend/purestack.studio/guides/plugins.mdx rename to frontend/purestack.studio/guides/extending/plugins.mdx index 05a5c75f..6c4a7d5e 100644 --- a/frontend/purestack.studio/guides/plugins.mdx +++ b/frontend/purestack.studio/guides/extending/plugins.mdx @@ -23,7 +23,7 @@ A plugin is a named object that extends how PureStack builds your site. Each of | Change how Markdown turns into HTML | [`markdown`](#transform-markdown) | | Build pages from data, such as one page per tag | [`pages`](#generate-pages) | | Change a whole page, header and footer included | [`hooks.onPageDocument`](#change-a-pages-dom) | -| Write extra files, such as a feed | [`hooks`](#write-extra-files) | +| Write extra files, such as a JSON page index | [`hooks`](#write-extra-files) | | Add styles or run code at other points in the build | [`hooks`](#run-code-during-the-build) | | Answer requests while you develop | [`devMiddleware`](#handle-requests-in-development) | @@ -48,10 +48,14 @@ This plugin adds a component that shows a release badge: import { definePlugin } from 'purestack' import { defineComponent, html } from 'regor' +export interface ReleaseBadge { + version?: string +} + export const sitePlugin = definePlugin({ name: 'site', components: () => ({ - releaseBadge: defineComponent<{ version?: string }>( + releaseBadge: defineComponent( html`v{{ version }}`, { props: ['version'] }, ), @@ -93,7 +97,7 @@ The badge appears on the page. Change its `tone` in `plugins/site.ts` while the - **Reloading.** `serve` loads the config again, and rebuilds the site, when the config or one of your files changes. A package loads once; restart `serve` after updating one. - **Mistakes.** When a reload fails, the terminal shows the error and the site keeps its last working plugins until you fix it. -Keep plugin code out of the content folder: TypeScript files there are [page scripts](/components/runtime/page-script/) for the browser. +Keep plugin code outside the content folder to make its build-time role clear. TypeScript files inside content are not automatically published; entries referenced by [PageScript](/components/runtime/page-script/) are bundled for the browser. `purestack.config.ts` is a build-time configuration file even though it lives inside content. ## Add a skin @@ -125,7 +129,7 @@ Select it in `siteConfig.json`: } ``` -Give a skin its own name; `standard` belongs to the built-in skin. The [Themes guide](./themes) covers palettes in depth. +Give a skin its own name; `standard` belongs to the built-in skin. The [Themes guide](../styling/themes) covers palettes in depth. ## Add components @@ -232,7 +236,7 @@ A transform also receives the file being processed. Its `path` is the page's pat | `path` | The content path the page takes, such as `blog/tags/regor.mdx`. | | `source` | The page's content, as a file at that path would hold it: frontmatter and Markdown. Give it as a string, or as a function that returns the string. | -The returned pages can be the source string or a function returning the source string. +Return an array of `{ path, source }` objects. Each object's `source` can be text or a function returning text, synchronously or asynchronously. Example tags plugin: @@ -283,7 +287,7 @@ export const apiPlugin = definePlugin({ }) ``` -A generated page works like a file at its path. `blog/tags/regor.mdx` is served at `/blog/tags/regor/`, shows up in automatic navigation, uses its folder's header and footer, and other pages can [link to it](./links) as `./tags/regor`. Its frontmatter can select a template or set `nav` options, as in any page. +A generated page works like a file at its path. `blog/tags/regor.mdx` is served at `/blog/tags/regor/`, shows up in automatic navigation, uses its folder's header and footer, and other pages can [link to it](../content/links) as `./tags/regor`. Its frontmatter can select a template or set `nav` options, as in any page. - **Paths.** A path ends in `.md`, `.mdx`, or `.rmdx` and stays inside the content folder. It cannot be a `header.mdx` or `footer.mdx`, or the path of a real file or of another plugin's page. - **Files.** `files` lists the pages in the content folder, each with its `relPath`, its `absPath`, and the `urlPath` it is served at. Generated pages are never among them. @@ -363,71 +367,25 @@ Use `markdown` instead to change only the page's content, and `onPageRendered` f ## Write extra files -A feed, a search index of your own, or any file built from the site's pages needs no special field. Collect pages as they render, and write the file once the build completes. Making the plugin a function lets each site pass its own options: +Collect page information in a page hook, then write a derived file after the full build. This example produces a JSON page index. Save it as `plugins/page-index.ts` beside the content directory: -```ts -// plugins/feed.ts -import { writeFile } from 'node:fs/promises' -import path from 'node:path' -import { definePlugin } from 'purestack' + -const escapeXml = (text: string) => - text.replace(/[<>&"]/g, (char) => `&#${char.charCodeAt(0)};`) - -export function feed(options: { siteUrl: string; folder?: string }) { - const folder = options.folder ?? '/blog/' - const posts = new Map() - return definePlugin({ - name: 'feed', - hooks: { - onConfigResolved() { - posts.clear() - }, - onPageRendered(context, page) { - const { title, date } = page.frontmatter - if (!page.urlPath.startsWith(folder) || !title || !date) return - posts.set(page.urlPath, { title, date: new Date(date as string | Date) }) - }, - async onBuildComplete(context) { - const { basePath, outDir, siteTitle } = context.config - const url = (urlPath: string) => `${options.siteUrl}${basePath}${urlPath}` - const items = [...posts] - .sort(([, a], [, b]) => b.date.getTime() - a.date.getTime()) - .map(([urlPath, post]) => - [ - '', - `${escapeXml(post.title)}`, - `${url(urlPath)}`, - `${post.date.toUTCString()}`, - '', - ].join(''), - ) - const rss = [ - '', - '', - `${escapeXml(siteTitle)}`, - `${url('/')}`, - ...items, - '', - ].join('\n') - await writeFile(path.join(outDir, 'feed.xml'), rss) - }, - }, - }) -} -``` +Add the plugin in `content/purestack.config.ts`: ```ts -// content/purestack.config.ts -export default defineConfig({ - plugins: [feed({ siteUrl: 'https://example.com' })], -}) +import { defineConfig } from 'purestack' +import { pageIndex } from '../plugins/page-index' + +export default defineConfig({ plugins: [pageIndex()] }) ``` -- **Every page is included.** Every page renders before `onBuildComplete` runs, and `onConfigResolved` empties the list when the next full build starts. -- **Write into `outDir`.** `context.config.outDir` points at `publishDir` during `publish`, so the file lands in the release folder too. -- **Links include `basePath`.** `page.urlPath` is the path inside the site; adding `basePath` gives the address visitors use. -- **While developing.** `serve` rewrites the file on full builds, not after each page edit. +After a build, open `page-index.json` in the output directory. Each entry contains a title and a public path. The plugin skips `index: false` pages and uses the same base-path helper as the framework. + +- **Start clean.** `onConfigResolved` clears the collected pages for each full build. +- **Use the resolved output path.** `context.config.outDir` points to `publishDir` during a release build. +- **Know the lifecycle.** `onBuildComplete` runs after the full build. This example does not rewrite its index after every incremental page edit; run a full build to refresh it. +- **Choose the data model.** This example keys entries by public URL. A hidden-URL multilingual site needs a locale-aware key if the index must retain every translation. ## Handle requests in development @@ -457,7 +415,7 @@ List plugins in the order they should apply: ```ts export default defineConfig({ - plugins: [sitePlugin, tagsPlugin, feed({ siteUrl: 'https://example.com' })], + plugins: [sitePlugin, tagsPlugin, pageIndex()], }) ``` @@ -473,14 +431,14 @@ export default defineConfig({ ## Share a plugin as a package -A plugin is a plain object, so any package can export one. The feed plugin above is already a function of its options, so it can move into a package unchanged, and each site lists it like a local plugin: +A plugin is a plain object, so a package can export one. The page-index plugin above is a factory and can be packaged with its types. The following uses `your-page-index-plugin` as a placeholder package name, not an existing PureStack dependency: ```ts import { defineConfig } from 'purestack' -import { feed } from 'purestack-feed' +import { pageIndex } from 'your-page-index-plugin' export default defineConfig({ - plugins: [feed({ siteUrl: 'https://example.com' })], + plugins: [pageIndex()], }) ``` @@ -520,7 +478,7 @@ Plugin "tags" generates "blog/first.mdx", which is already a content file. When a hook or page generator throws, the error names the plugin and where it failed: ```text -Plugin "feed" failed in onBuildComplete: ENOENT: no such file or directory +Plugin "page-index" failed in onBuildComplete: ENOENT: no such file or directory ``` While `serve` runs, a failing page hook shows its error on that page and leaves the server running. Most other problems are one of these: diff --git a/frontend/purestack.studio/guides/extending/plugins/page-index.ts b/frontend/purestack.studio/guides/extending/plugins/page-index.ts new file mode 100644 index 00000000..dae6a93c --- /dev/null +++ b/frontend/purestack.studio/guides/extending/plugins/page-index.ts @@ -0,0 +1,35 @@ +import { writeFile } from 'node:fs/promises' +import path from 'node:path' +import { withBasePath } from '@purestack/ts-util' +import { definePlugin } from 'purestack' + +export function pageIndex() { + const pages = new Map() + + return definePlugin({ + name: 'page-index', + hooks: { + onConfigResolved() { + pages.clear() + }, + onPageRendered(_context, page) { + if (page.frontmatter.index === false) return + pages.set(page.urlPath, page.frontmatter.title ?? page.urlPath) + }, + async onBuildComplete({ config }) { + const entries = [...pages] + .sort(([left], [right]) => left.localeCompare(right)) + .map(([urlPath, title]) => ({ + title, + url: withBasePath(config.basePath, urlPath), + })) + + await writeFile( + path.join(config.outDir, 'page-index.json'), + `${JSON.stringify(entries, null, 2)}\n`, + 'utf8', + ) + }, + }, + }) +} diff --git a/frontend/purestack.studio/guides/regor-launch-checklist.ts b/frontend/purestack.studio/guides/extending/regor-launch-checklist.ts similarity index 100% rename from frontend/purestack.studio/guides/regor-launch-checklist.ts rename to frontend/purestack.studio/guides/extending/regor-launch-checklist.ts diff --git a/frontend/purestack.studio/guides/regor.mdx b/frontend/purestack.studio/guides/extending/regor.mdx similarity index 83% rename from frontend/purestack.studio/guides/regor.mdx rename to frontend/purestack.studio/guides/extending/regor.mdx index 0e8d3162..dc6ad089 100644 --- a/frontend/purestack.studio/guides/regor.mdx +++ b/frontend/purestack.studio/guides/extending/regor.mdx @@ -155,7 +155,7 @@ Write components in an `.mdx`, `.rmdx`, or `.md` page as you would write HTML el

Before you begin

Keep your site configuration beside the content folder.

- Set up the site + Set up the site
``` @@ -179,7 +179,7 @@ Text in double braces is a Regor expression, anywhere in a page. Inside markup, | Field | Contents | | --- | --- | | `tsSsgContext.pageInfo.frontmatter` | The page's frontmatter, including any custom fields you add | -| `tsSsgContext.pageInfo.urlPath` | The page's URL, such as `/guides/regor/` | +| `tsSsgContext.pageInfo.urlPath` | The page's URL, such as `/guides/extending/regor/` | | `tsSsgContext.pageInfo.relPath` | The source file path inside the content folder | | `tsSsgContext.site` | The resolved site configuration | | `tsSsgContext.navigation`, `tsSsgContext.outline` | The page's navigation and heading outline | @@ -260,17 +260,19 @@ export function defineReleaseNoteComponents() { } ``` -Register it in a build script that calls `buildSite`. Components you pass in `components` are added to the built-in ones: +Save the component module as `components/releaseNote.ts` beside your `content` directory. Register its factory in `content/purestack.config.ts` so the CLI loads it for builds and development: ```ts -import { buildSite } from '@purestack/ts-ssg' -import { defineReleaseNoteComponents } from './components/releaseNote' - -await buildSite({ - siteConfig: { contentDir: './content' }, - options: { - components: { ...defineReleaseNoteComponents() }, - }, +import { defineConfig, definePlugin } from 'purestack' +import { defineReleaseNoteComponents } from '../components/releaseNote' + +export default defineConfig({ + plugins: [ + definePlugin({ + name: 'release-notes', + components: () => defineReleaseNoteComponents(), + }), + ], }) ``` @@ -281,7 +283,7 @@ await buildSite({ > Export the interface that types a component, as ReleaseNote is exported here. The - VS Code extension finds components + VS Code extension finds components through their exported interfaces. Without the export, it can't complete the component's props or jump from a tag in your markup to the component's source. @@ -308,112 +310,7 @@ Place a `RegorApp` in a page and point it at a TypeScript file next to the page. ``` -```ts -import { - defineBadgeComponents, - defineButtonComponents, - defineFlexComponents, - defineFormComponents, - defineIconComponents, - definePanelComponents, -} from '@purestack/ts-components' -import { tabler_rocket } from '@purestack/ts-svg-icons' -import { - batch, - type ComputedRef, - computed, - createApp, - defineComponent, - html, - type Ref, - ref, -} from 'regor' - -export interface Task { - id: string - title: string - done: Ref -} - -export interface TaskRow { - task: Task -} - -const taskRow = defineComponent( - html``, - { props: ['task'] }, -) - -export interface LaunchChecklist { - tasks: Task[] - completed: ComputedRef - ready: ComputedRef - launched: Ref - launch: () => void - reset: () => void -} - -const launchChecklistTemplate = html` - - - Launch checklist - - {{ completed }} of {{ tasks.length }} done - - - - - Launch - Start over - - Launched. Every check passed. - -` - -function createLaunchChecklist(): LaunchChecklist { - const tasks: Task[] = [ - { id: 'docs', title: 'Docs reviewed', done: ref(true) }, - { id: 'tests', title: 'Tests passing', done: ref(false) }, - { id: 'notes', title: 'Release notes written', done: ref(false) }, - ] - const launched = ref(false) - const completed = computed(() => tasks.filter((task) => task.done()).length) - const ready = computed(() => completed() === tasks.length) - return { - tasks, - completed, - ready, - launched, - launch: () => launched(true), - reset: () => - batch(() => { - for (const task of tasks) task.done(false) - launched(false) - }), - } -} - -createApp( - { - components: { - LaunchChecklist: defineComponent( - launchChecklistTemplate, - { context: createLaunchChecklist }, - ), - TaskRow: taskRow, - ...defineBadgeComponents(), - ...defineButtonComponents(), - ...defineFlexComponents(), - ...defineFormComponents(), - ...defineIconComponents((name) => - name === 'tabler:rocket' ? tabler_rocket : '', - ), - ...definePanelComponents(), - }, - }, - { selector: 'app#launch-checklist', template: html`` }, -) -``` + The app is an ordinary module. You can split it into several files, import npm packages, and share code with your build-time components. PureStack bundles it with esbuild when it builds the page. @@ -424,7 +321,7 @@ Browser apps do not get the build's component list. Each app registers the compo Register each icon your app shows, including the defaults that components draw themselves, such as the arrow in a select field. The - Icons guide lists those + Icons guide lists those defaults. @@ -575,12 +472,12 @@ These patterns scale from a single widget to a multi-page admin tool with tables ## Use other libraries -A [PageScript](/components/runtime/page-script/) loads any TypeScript entry point. Use it for small enhancements to the page's HTML, or to mount a component from another framework. PureStack bundles the script and its npm dependencies, so you can use React, Vue, a charting library, or no library at all. +A [PageScript](/components/runtime/page-script/) loads a browser entry point. Use it to enhance existing HTML or mount a browser-compatible library. PureStack bundles local TypeScript and its imports with esbuild; framework-specific source formats may need an additional compilation step. Start with a plain TypeScript module, as shown in [runtime scripts](../../components/runtime). Other frameworks can't use the built-in components, and they don't inherit the theme automatically. Use - semantic tone classes or palette + semantic tone classes or palette variables to keep them visually consistent. diff --git a/frontend/purestack.studio/guides/getting-started/_nav.json b/frontend/purestack.studio/guides/getting-started/_nav.json new file mode 100644 index 00000000..a6c809b3 --- /dev/null +++ b/frontend/purestack.studio/guides/getting-started/_nav.json @@ -0,0 +1,8 @@ +{ + "sequence": [ + "index.mdx", + "purestack-cli.mdx", + "site-config.mdx", + "vscode-extension.mdx" + ] +} diff --git a/frontend/purestack.studio/guides/getting-started/index.mdx b/frontend/purestack.studio/guides/getting-started/index.mdx new file mode 100644 index 00000000..70d637ed --- /dev/null +++ b/frontend/purestack.studio/guides/getting-started/index.mdx @@ -0,0 +1,17 @@ +--- +title: Getting started +description: Run the CLI, configure your site, and set up the editor. +template: doc +nav: + icon: tabler:rocket +layout: + showToc: true +--- + +# Getting started + +Run the CLI, configure your site, and set up the editor. + +- [PureStack CLI](./purestack-cli) +- [Site configuration](./site-config) +- [VS Code extension](./vscode-extension) diff --git a/frontend/purestack.studio/guides/purestack-cli.mdx b/frontend/purestack.studio/guides/getting-started/purestack-cli.mdx similarity index 92% rename from frontend/purestack.studio/guides/purestack-cli.mdx rename to frontend/purestack.studio/guides/getting-started/purestack-cli.mdx index d3e6bc5e..91a8188c 100644 --- a/frontend/purestack.studio/guides/purestack-cli.mdx +++ b/frontend/purestack.studio/guides/getting-started/purestack-cli.mdx @@ -42,7 +42,7 @@ The CLI requires `siteConfig.json` at the root of the directory passed to `--con } ``` -Save that JSON as `content/siteConfig.json`. The two relative output paths are resolved from `content`, so they point to `dist/site` and `dist/publish` beside it. Light and dark theme styles are generated by default. The [Site configuration guide](/guides/site-config/) explains every setting, including navigation, search, sitemap, localization, and consent. +Save that JSON as `content/siteConfig.json`. The two relative output paths are resolved from `content`, so they point to `dist/site` and `dist/publish` beside it. Light and dark theme styles are generated by default. The [Site configuration guide](/guides/getting-started/site-config/) explains every setting, including navigation, search, sitemap, localization, and consent. Now add a home page at `content/index.mdx`: @@ -70,7 +70,7 @@ Open the URL printed as `serving at` after the first build. With the default set ## Understand the content folder -The CLI recognizes `.md`, `.mdx`, and `.rmdx` as pages. Both `.mdx` and `.rmdx` use the Regor MDX pipeline. `.md` also uses that pipeline by default; `mdx.compileMdAsMdx` can change how `.md` is compiled. See the [Regor guide](/guides/regor/) for markup and component usage. +The CLI recognizes `.md`, `.mdx`, and `.rmdx` as pages. Both `.mdx` and `.rmdx` use the Regor MDX pipeline. `.md` also uses that pipeline by default; `mdx.compileMdAsMdx` can change how `.md` is compiled. See the [Regor guide](/guides/extending/regor/) for markup and component usage. Routes follow filenames and folders: @@ -86,9 +86,9 @@ Routes follow filenames and folders: Set `index: false` in frontmatter for a preview or unlisted page. PureStack still builds it and resolves links to it, but excludes it from generated navigation, search, and the sitemap and adds a robots `noindex` tag. `hidden: true` only hides a page from navigation. -Files such as `assets/logo.png` are copied into the output folder with their relative paths intact. The root `siteConfig.json` and navigation files are inputs to the build, not public assets. You can place `_nav.json` in a folder for custom or hybrid navigation, and `header.mdx` or `footer.mdx` for shared page sections. The [Site configuration guide](/guides/site-config/) shows how to enable and order navigation. A Markdown or MDX file whose name, or a folder above it, starts with `_` is never a page; pages show it with [``](/guides/shared-content/). +Files such as `assets/logo.png` are copied into the output folder with their relative paths intact. The root `siteConfig.json` and navigation files are inputs to the build, not public assets. You can place `_nav.json` in a folder for custom or hybrid navigation, and `header.mdx` or `footer.mdx` for shared page sections. The [Site configuration guide](/guides/getting-started/site-config/) shows how to enable and order navigation. A Markdown or MDX file whose name, or a folder above it, starts with `_` is never a page; pages show it with [``](/guides/content/shared-content/). -A `purestack.config.ts` beside `siteConfig.json` adds code of your own, such as a brand skin, components, templates, or generated pages, through [plugins](/guides/plugins/). Every command loads it when it is present; like other `.ts` files in the content folder, it is never copied to the output. +A `purestack.config.ts` beside `siteConfig.json` adds code of your own, such as a brand skin, components, templates, or generated pages, through [plugins](/guides/extending/plugins/). Every command loads it when it is present; like other `.ts` files in the content folder, it is never copied to the output. ## Commands at a glance @@ -161,7 +161,7 @@ The output directory is a filesystem path; `basePath` is a URL path. For a site } ``` -The home page stays at `dist/site/index.html`, but links and the development server use `/docs/`. Your production host must serve the published folder at `/docs/`. `sitemap.baseUrl` supplies the origin; PureStack combines it with `basePath` for sitemap and canonical URLs. See [Site configuration](/guides/site-config/) for the full path and SEO rules. +The home page stays at `dist/site/index.html`, but links and the development server use `/docs/`. Your production host must serve the published folder at `/docs/`. `sitemap.baseUrl` supplies the origin; PureStack combines it with `basePath` for sitemap and canonical URLs. See [Site configuration](/guides/getting-started/site-config/) for the full path and SEO rules. ## When a command fails diff --git a/frontend/purestack.studio/guides/site-config.mdx b/frontend/purestack.studio/guides/getting-started/site-config.mdx similarity index 98% rename from frontend/purestack.studio/guides/site-config.mdx rename to frontend/purestack.studio/guides/getting-started/site-config.mdx index ea0c33cf..7ed61ea8 100644 --- a/frontend/purestack.studio/guides/site-config.mdx +++ b/frontend/purestack.studio/guides/getting-started/site-config.mdx @@ -15,7 +15,7 @@ nav: `siteConfig.json` is the site-wide starting point for a PureStack build. It controls where files are written, which styles and navigation are generated, and which optional features appear on every page. Page frontmatter still controls the title, description, template, and layout of an individual page. -Put the file at the root of the content directory passed to the [PureStack CLI](/guides/purestack-cli/): +Put the file at the root of the content directory passed to the [PureStack CLI](/guides/getting-started/purestack-cli/): ```text content/ @@ -119,7 +119,7 @@ PureStack generates a stylesheet for each configured theme. `light` and `dark` a `fileName` names the generated CSS file; `href` is the public stylesheet path inserted into pages. When `href` is omitted, it defaults to `/assets/` followed by `fileName`. If you set `href` explicitly, keep it aligned with the generated file. PureStack derives dark and any additional theme URLs from that light-theme `href`. `pretty` formats generated CSS with Prettier when `true`; it defaults to `false`. -`theme.skin` selects a registered skin. A skin supplies the light and dark palettes; `theme.presets` and explicit theme tokens refine them. `remSize` and `mobileRemSize` optionally set the root font size. See [Themes](/guides/themes/) for custom skins and palette examples. +`theme.skin` selects a registered skin. A skin supplies the light and dark palettes; `theme.presets` and explicit theme tokens refine them. `remSize` and `mobileRemSize` optionally set the root font size. See [Themes](/guides/styling/themes/) for custom skins and palette examples. ## Navigation and page contents @@ -190,7 +190,7 @@ Place this in `content/guides/_nav.json`. A custom item's `url` is relative to t } ``` -`mdx.highlighter` accepts `highlightjs` (the default and faster option) or `shiki`. `disableHighlighter` turns highlighting off. `compileMdAsMdx` defaults to `true`, so `.md` files use the MDX/Regor markup pipeline; `.mdx` files use that pipeline in either case. The [Regor guide](/guides/regor/) explains the supported markup and components. +`mdx.highlighter` accepts `highlightjs` (the default and faster option) or `shiki`. `disableHighlighter` turns highlighting off. `compileMdAsMdx` defaults to `true`, so `.md` files use the MDX/Regor markup pipeline; `.mdx` files use that pipeline in either case. The [Regor guide](/guides/extending/regor/) explains the supported markup and components. ## Search diff --git a/frontend/purestack.studio/guides/vscode-extension.mdx b/frontend/purestack.studio/guides/getting-started/vscode-extension.mdx similarity index 98% rename from frontend/purestack.studio/guides/vscode-extension.mdx rename to frontend/purestack.studio/guides/getting-started/vscode-extension.mdx index 451faa46..592273d8 100644 --- a/frontend/purestack.studio/guides/vscode-extension.mdx +++ b/frontend/purestack.studio/guides/getting-started/vscode-extension.mdx @@ -110,7 +110,7 @@ layout: # Project guide ``` -Suggestions can appear as you type a key, `:`, or a new line in frontmatter. This type-aware help needs `PageFrontmatter` source available in the opened workspace or installed `@purestack/ts-common` package. The [Regor guide](/guides/regor/) explains how the page is rendered. +Suggestions can appear as you type a key, `:`, or a new line in frontmatter. This type-aware help needs `PageFrontmatter` source available in the opened workspace or installed `@purestack/ts-common` package. The [Regor guide](/guides/extending/regor/) explains how the page is rendered. ## Format markup on save diff --git a/frontend/purestack.studio/guides/index.mdx b/frontend/purestack.studio/guides/index.mdx index 32f3c967..0171e23d 100644 --- a/frontend/purestack.studio/guides/index.mdx +++ b/frontend/purestack.studio/guides/index.mdx @@ -1,6 +1,6 @@ --- title: Guides -description: Learn how to build and style a PureStack site with the CLI, site configuration, links, code blocks, shared content, VS Code extension, Regor, typography, utility classes, semantic tones, icons, themes, and plugins. +description: Learn PureStack through guides for getting started, content, styling, extending the framework, and publishing. template: doc layout: showNav: true @@ -13,74 +13,33 @@ nav: # Build with PureStack -These guides follow the path from a content folder to a finished interface. Start with the site generator, then learn how to compose pages and give them a consistent visual language. +Start with the CLI and site configuration, then choose a topic below. Each category groups the guides for one part of building a PureStack site. -

PureStack CLI

-

Create routes from Markdown and MDX, preview locally, and publish static output.

- Use the CLI +

Getting started

+

Run the CLI, configure your site, and set up the editor.

+ Explore getting started
-

Site configuration

-

Set output paths, navigation, themes, search, metadata, languages, and optional scripts.

- Configure a site +

Content

+

Write links and code examples, share content, and organize translations.

+ Explore content
-

Links

-

Link pages and sections relative to your file, and let every build check them.

- Write links +

Styling

+

Work with typography, utility classes, semantic tones, icons, and themes.

+ Explore styling
-

Code blocks

-

Show code in a page, or import it from a file so it always matches the source.

- Show code +

Extending PureStack

+

Add browser behavior with Regor and extend the build with plugins.

+ Explore extending purestack
-

Shared content

-

Write a passage once and show it on every page it belongs to.

- Share a passage -
- -

VS Code extension

-

Find component source, complete props and frontmatter, and format markup on save.

- Set up the editor - Install from Marketplace -
- -

Regor

-

Render pages at build time and add interactive apps in the browser with one component model.

- Learn Regor -
- -

Typography

-

Use semantic headings and the shared type scale to keep pages easy to scan.

- Shape your type -
- -

Utility CSS classes

-

Compose spacing, type, layout, borders, visibility, and theme-aware tones with the generated class set.

- Use utility classes -
- -

Semantic tones

-

Compare all ten tones, see their surface and button roles, and try interactive states.

- Explore tones -
- -

Icons

-

Find icon names, size and color icons with the text, and register them in browser apps.

- Add icons -
- -

Themes

-

Start with a skin, tune light and dark palettes, and add theme-aware styles.

- Style a site -
- -

Plugins

-

Add your own skin, components, page templates, and build steps as named plugins.

- Extend a site +

Publishing

+

Configure search and metadata, then prepare and verify a release artifact.

+ Explore publishing
diff --git a/frontend/purestack.studio/guides/publishing/_nav.json b/frontend/purestack.studio/guides/publishing/_nav.json new file mode 100644 index 00000000..4639a034 --- /dev/null +++ b/frontend/purestack.studio/guides/publishing/_nav.json @@ -0,0 +1,7 @@ +{ + "sequence": [ + "index.mdx", + "search-and-metadata.mdx", + "deployment.mdx" + ] +} diff --git a/frontend/purestack.studio/guides/publishing/deployment.mdx b/frontend/purestack.studio/guides/publishing/deployment.mdx new file mode 100644 index 00000000..95d2c7a1 --- /dev/null +++ b/frontend/purestack.studio/guides/publishing/deployment.mdx @@ -0,0 +1,81 @@ +--- +title: Deployment +description: Prepare and inspect a production artifact, configure a static host, and diagnose differences from local development. +template: doc +nav: + icon: tabler:rocket +layout: + showToc: true +--- + +# Deployment + +`purestack publish` prepares a local release directory. Your hosting provider or deployment pipeline uploads that directory. Before uploading, verify the generated artifact with the same public path and routing that the production host will use. + +## Keep source and output separate + +Set explicit output paths in `content/siteConfig.json`: + +```json +{ + "siteTitle": "Acme Docs", + "outDir": "../dist/site", + "publishDir": "../dist/publish" +} +``` + +These paths resolve from the content directory. `build` and `serve` use `dist/site`; `publish` uses `dist/publish`. Keep both outside `content` so generated assets are not rediscovered as source assets. `publish` cleans `publishDir`, so that directory must contain only disposable build output. + +## Build the release + +From the project root, using your installed CLI: + +```sh +yarn purestack publish --content ./content +``` + +The command cleans the release directory, minifies generated HTML and bundled browser scripts, disables CSS pretty printing, and treats script asset failures as errors. It loads `content/purestack.config.ts` if present. Plugins writing extra files should use `context.config.outDir`, which points to `publishDir` for this build. + +The artifact contains route directories with `index.html`, generated theme styles under `assets/`, copied static assets, referenced browser bundles, and optional Pagefind, sitemap, and robots outputs. Deploy the complete artifact so HTML and the script filenames it references come from the same build. + +## Configure the public mount path + +| Public site | `basePath` | `sitemap.baseUrl` | Host mount | +| --- | --- | --- | --- | +| `https://docs.example.com/` | Omit or empty | `https://docs.example.com` | Artifact root at `/` | +| `https://example.com/docs/` | `/docs` | `https://example.com` | Artifact root at `/docs/` | + +`basePath` changes URLs, not the directory tree in the artifact. It does not create `dist/publish/docs/`. Configure the host to serve `dist/publish/index.html` at `/docs/` and nested route directories at their corresponding URLs. + +A static host must serve directory indexes: `/guides/install/` maps to `guides/install/index.html`. Check a nested URL directly, including after a browser refresh. [Hidden locale URLs](../content/localization#choose-a-url-strategy) need additional host-side language routing; prefixed locale URLs already map to separate directories. + +## Verify the release rather than only the preview + +`purestack serve` is a development server that rebuilds into `outDir`; it is not a command for serving an existing release directory unchanged. Use your host's local preview or a static file server pointed at `publishDir` to inspect the artifact over HTTP. + +Before upload, verify these concrete outcomes: + +1. The home page and a nested page load directly at the intended base path. +2. Light and dark styles, images, and browser scripts return successfully. +3. One interactive example responds to input with no browser console error. +4. Search returns a known page, if enabled. Read Pagefind diagnostics even if the build succeeded. +5. Canonical and sitemap URLs use the production origin, and preview images exist. +6. Pages marked `index: false` are absent from the sitemap and search index. + +The generated HTML contains build-time values and public browser code. Do not put private credentials in frontmatter or browser entry points. `auth.enabled` controls account UI; enforce actual access restrictions in your application or host. + +## Account for development-only behavior + +Live reload, diagnostic error pages, watched rebuilds, locale preference handling, and plugin `devMiddleware` belong to the development server. They are not a production server shipped by `publish`. A browser app calling a mock endpoint needs a corresponding deployed endpoint or an explicit production API URL. + +## Troubleshoot a deployment + +| Symptom | Likely check | +| --- | --- | +| Home works, nested route is 404 | Host directory-index routing and mount path | +| HTML loads without styles | `basePath`, `style.href`, and uploaded `assets/` directory | +| Browser script is 404 | HTML and bundles came from different builds, or the mount path differs | +| Search UI opens but finds nothing | Pagefind build diagnostics and uploaded `pagefind/` files | +| Deleted pages remain available | Deploy a clean release artifact and check the host's stale-file/cache handling | +| A draft is public | `draft` only hides generated navigation; remove unpublished source from the public build | +| A local API stops working | Replace development middleware with a deployed service | diff --git a/frontend/purestack.studio/guides/publishing/index.mdx b/frontend/purestack.studio/guides/publishing/index.mdx new file mode 100644 index 00000000..4d532486 --- /dev/null +++ b/frontend/purestack.studio/guides/publishing/index.mdx @@ -0,0 +1,16 @@ +--- +title: Publishing +description: Configure search and metadata, then prepare and verify a release artifact. +template: doc +nav: + icon: tabler:cloud-upload +layout: + showToc: true +--- + +# Publishing + +Configure search and metadata, then prepare and verify a release artifact. + +- [Search and metadata](./search-and-metadata) +- [Deployment](./deployment) diff --git a/frontend/purestack.studio/guides/publishing/search-and-metadata.mdx b/frontend/purestack.studio/guides/publishing/search-and-metadata.mdx new file mode 100644 index 00000000..645b853a --- /dev/null +++ b/frontend/purestack.studio/guides/publishing/search-and-metadata.mdx @@ -0,0 +1,110 @@ +--- +title: Search and metadata +description: Configure Pagefind, control indexing, and verify canonical URLs, social previews, sitemap, and robots output. +template: doc +nav: + icon: tabler:search +layout: + showToc: true +--- + +# Search and metadata + +PureStack can generate a Pagefind search index, page metadata, a sitemap, and `robots.txt`. These are separate outputs: hiding a page from navigation does not remove it from search, and enabling a sitemap does not enable access restrictions. + +## Enable search and verify a result + +Pagefind is enabled by default. This configuration excludes a section from search: + +```json +{ + "pagefind": { + "enabled": true, + "excludePaths": ["/internal-notes/"] + } +} +``` + +The build writes search assets into `pagefind/` in the output directory. The [SearchBox component](/components/site/search-box/) supplies the built-in search UI; keep it in your header if you replace the default top bar. + +After building, serve the output over HTTP, search for a distinctive phrase from a public page, and open the result. Include the `pagefind/` directory in deployment. Setting `enabled: false` disables indexing and removes its generated output during a full index build. + +**Read the build's search diagnostics.** The Pagefind integration catches indexing errors and logs them; a successful build exit does not guarantee a usable search index. If search is empty, check the indexing log, emitted files, exclusion settings, and browser network requests. + +## Decide what should be indexed + +Use `index: false` in a page's frontmatter when the page should be excluded from generated navigation, Pagefind, and the sitemap. It also adds a robots `noindex` meta tag. The HTML remains accessible at its route. + +`pagefind.excludePaths` affects only search. Prefixes are matched against routes derived from HTML paths relative to the output directory. Do not include `basePath`; for localized output, include the locale directory, such as `/de/internal-notes/`. Missing leading or trailing slashes are normalized. + +`hidden: true` and `draft: true` only exclude a page from generated navigation. They do not exclude it from search or sitemap generation. + +## Set the public origin + +For `https://example.com/docs/`, merge this into `siteConfig.json`: + +```json +{ + "basePath": "/docs", + "sitemap": { + "enabled": true, + "baseUrl": "https://example.com" + } +} +``` + +Use the origin for `baseUrl` and the mount path for `basePath`. A page at `/guides/install/` gets the public address `https://example.com/docs/guides/install/`. The sitemap and canonical URL use these settings. The origin also makes relative social-preview images absolute. Setting `baseUrl` is useful for metadata even when sitemap generation is disabled. + +## Give each page useful metadata + +In a page's frontmatter: + +```yaml +title: Installation +description: Install Acme and verify your first project build. +preview: + image: /assets/install-preview.png + imageAlt: The completed Acme project setup + imageWidth: 1200 + imageHeight: 630 +``` + +Put the image at `content/assets/install-preview.png`. The configuration describes an existing image; it does not generate one. + +| Output | Precedence | +| --- | --- | +| Browser title | `siteTitle | page title`, or the available title when only one is set | +| Description meta tag | Page `description`, subject to explicit `head` overrides | +| Social title | Page `preview.title`, page `title`, site `preview.title`, site title | +| Social description | Page `preview.description`, page `description`, site `preview.description` | +| Social image | Page `preview.image`, then site `preview.image` | +| Open Graph site name | Site `preview.siteName`, then `siteTitle` | + +A page-specific image uses its own dimensions; it does not inherit the site image's width and height. Set both when overriding an image. `preview` also supports image alt text, type, locale, and Twitter card/account fields. Explicit `head` settings merge over the generated head configuration; `index: false` still forces `robots: noindex` afterward. + +For a site-wide fallback, add `preview.image`, `imageAlt`, dimensions, and `siteName` to `siteConfig.json`. Keep page titles and descriptions specific to their content so shared previews identify the destination. + +## Generate sitemap and robots output + +Sitemap generation defaults to off. When enabled, `sitemap.baseUrl` is required. The default filenames are `sitemap.xml` and `robots.txt`; robots generation defaults to on within the enabled sitemap feature. + +```json +{ + "sitemap": { + "enabled": true, + "baseUrl": "https://example.com", + "robots": { + "enabled": true, + "userAgent": "*", + "allow": ["/"], + "disallow": ["/internal-notes/"] + } + } +} +``` + +Robots rules do not prevent PureStack from generating a file. They also do not remove it from the sitemap; use `index: false` for that. Robots output is tied to sitemap generation, so enabling `robots` alone produces no file. The [configuration reference](../getting-started/site-config#sitemap-robots-and-social-previews) covers additional directives. + +## Inspect the artifact + +Open a generated HTML file and check its title, description, canonical link, Open Graph tags, and Twitter tags. Confirm that the preview image URL is reachable. Check a sitemap entry for the right origin, base path, and locale. For an `index: false` page, confirm its HTML exists, contains `noindex`, and is absent from search and the sitemap. diff --git a/frontend/purestack.studio/guides/styling/_nav.json b/frontend/purestack.studio/guides/styling/_nav.json new file mode 100644 index 00000000..6125aff4 --- /dev/null +++ b/frontend/purestack.studio/guides/styling/_nav.json @@ -0,0 +1,10 @@ +{ + "sequence": [ + "index.mdx", + "typography.mdx", + "utility-css-classes.mdx", + "semantic-tones.mdx", + "icons.mdx", + "themes.mdx" + ] +} diff --git a/frontend/purestack.studio/guides/icons.mdx b/frontend/purestack.studio/guides/styling/icons.mdx similarity index 98% rename from frontend/purestack.studio/guides/icons.mdx rename to frontend/purestack.studio/guides/styling/icons.mdx index 8519d273..c7d867b1 100644 --- a/frontend/purestack.studio/guides/icons.mdx +++ b/frontend/purestack.studio/guides/styling/icons.mdx @@ -108,7 +108,7 @@ Icons are drawn in the current text color, so an icon matches the label beside i