Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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) |

Expand All @@ -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.

</details>
Expand Down
2 changes: 1 addition & 1 deletion frontend/purestack.studio/components/_nav.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"pageLinks": true,
"sequence": ["index.mdx", "guides"],
"sequence": ["guides", "index.mdx"],
"items": [
{
"id": "guides",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,14 @@ nav:
## In context

<ComponentExample label="A complete message" caption="Every content feature is visible: eyebrow, title, badge, icon, body, metadata, and the actions slot.">
<div class="component-recipe"><AlertBox tone="info" variant="spotlight" eyebrow="DESIGN SYSTEM" title="Make feedback useful" badge="Tip" icon="lucide:check" meta="Semantic tones adapt to light and dark themes."><p>Explain what happened, why it matters, and what the reader can do next.</p><template #actions><BtnLink href="/guides/semantic-tones/" tone="info" variant="outline">Explore semantic tones</BtnLink></template></AlertBox></div>
<div class="component-recipe"><AlertBox tone="info" variant="spotlight" eyebrow="DESIGN SYSTEM" title="Make feedback useful" badge="Tip" icon="lucide:check" meta="Semantic tones adapt to light and dark themes."><p>Explain what happened, why it matters, and what the reader can do next.</p><template #actions><BtnLink href="/guides/styling/semantic-tones/" tone="info" variant="outline">Explore semantic tones</BtnLink></template></AlertBox></div>
</ComponentExample>

```html
<AlertBox tone="info" variant="spotlight" eyebrow="DESIGN SYSTEM" title="Make feedback useful" badge="Tip" icon="lucide:check" meta="Semantic tones adapt to light and dark themes.">
<p>Explain what happened, why it matters, and what the reader can do next.</p>
<template #actions>
<BtnLink href="/guides/semantic-tones/" tone="info" variant="outline">Explore semantic tones</BtnLink>
<BtnLink href="/guides/styling/semantic-tones/" tone="info" variant="outline">Explore semantic tones</BtnLink>
</template>
</AlertBox>
```
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,15 @@ Use BtnLink to navigate to a page, a section, or an external destination. For an
## In context

<ComponentExample label="A useful destination" caption="These are real links. Use the keyboard or your browser’s open-in-new-tab action to follow them.">
<div class="component-recipe"><Badge tone="feature">START HERE</Badge><h3>Build your first interface</h3><p>Learn how static content and interactive components fit together.</p><BtnGroup :wrap="true"><BtnLink href="/guides/regor/" tone="accent">Explore Regor</BtnLink><BtnLink href="/components/" variant="outline">Browse components</BtnLink></BtnGroup></div>
<div class="component-recipe"><Badge tone="feature">START HERE</Badge><h3>Build your first interface</h3><p>Learn how static content and interactive components fit together.</p><BtnGroup :wrap="true"><BtnLink href="/guides/extending/regor/" tone="accent">Explore Regor</BtnLink><BtnLink href="/components/" variant="outline">Browse components</BtnLink></BtnGroup></div>
</ComponentExample>

```html
<Badge tone="feature">START HERE</Badge>
<h3>Build your first interface</h3>
<p>Learn how static content and interactive components fit together.</p>
<BtnGroup :wrap="true">
<BtnLink href="/guides/regor/" tone="accent">Explore Regor</BtnLink>
<BtnLink href="/guides/extending/regor/" tone="accent">Explore Regor</BtnLink>
<BtnLink href="/components/" variant="outline">Browse components</BtnLink>
</BtnGroup>
```
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ const TREES: Record<TreeName, NavItem[]> = {
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',
Expand Down Expand Up @@ -143,7 +143,7 @@ const TREES: Record<TreeName, NavItem[]> = {

const DEFAULTS = {
treeName: 'docs' as TreeName,
currentUrl: '/guides/themes/',
currentUrl: '/guides/styling/themes/',
showIcons: true,
tone: 'neutral' as SemanticTone,
variant: 'flat' as ComponentVariant,
Expand Down
18 changes: 5 additions & 13 deletions frontend/purestack.studio/guides/_nav.json
Original file line number Diff line number Diff line change
Expand Up @@ -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": [
{
Expand Down
9 changes: 9 additions & 0 deletions frontend/purestack.studio/guides/content/_nav.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"sequence": [
"index.mdx",
"links.mdx",
"code-blocks.mdx",
"shared-content.mdx",
"localization.mdx"
]
}
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
18 changes: 18 additions & 0 deletions frontend/purestack.studio/guides/content/index.mdx
Original file line number Diff line number Diff line change
@@ -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)
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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.

Expand Down Expand Up @@ -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

Expand Down
78 changes: 78 additions & 0 deletions frontend/purestack.studio/guides/content/localization.mdx
Original file line number Diff line number Diff line change
@@ -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.
Original file line number Diff line number Diff line change
Expand Up @@ -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.** `<import-content>` 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 `<import-content>`. Add `content/header.mdx` to supply the site's header:

```mdx
<TopBar tone="neutral" variant="surface" />
```

Add `content/footer.mdx` for its footer:

```mdx
<SiteFooter copyright="Acme documentation" />
```

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:
Expand Down
7 changes: 7 additions & 0 deletions frontend/purestack.studio/guides/extending/_nav.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"sequence": [
"index.mdx",
"regor.mdx",
"plugins.mdx"
]
}
16 changes: 16 additions & 0 deletions frontend/purestack.studio/guides/extending/index.mdx
Original file line number Diff line number Diff line change
@@ -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)
Loading
Loading