+
{option.description}
+` and filling in that locale's
- `code.json` — the component code itself doesn't change.
-- **Theme chrome** (pagination, admonition labels, sidebar buttons, etc.): never
- override these in this site's own `code.json`. Docusaurus ships its own
- professionally translated defaults per locale; only `homepage.*`/`framework.*` keys
- belong in this site's `code.json`.
-- Adding a third locale: add it to `i18n.locales` (and `localeConfigs`) in
- `docusaurus.config.ts`, run `write-translations`, translate `code.json` plus
- `docusaurus-theme-classic/{navbar,footer}.json`. Docs stay English until someone
- actually translates them into the new `i18n//docusaurus-plugin-content-docs/current/`
- tree.
-- **Translation quality: never translate literally.** When writing or updating any
- `code.json` (or any other translated string) for `es` or any future locale, use the
- term a professional in that field, in that language, actually uses — not a word-for-word
- rendering of the English. Before translating a technical or domain term (Unity/UdonSharp/
- VRChat/GitHub concepts especially), check how it's rendered in that language's official or
- most-trusted sources (e.g. Unity's own localized manual at `docs.unity3d.com/es/...`, VRChat's
- Spanish-speaking creator community, GitHub's own glossary/docs) rather than guessing or
- machine-translating. Two patterns come up constantly and cut opposite ways, so check per
- term instead of assuming:
- - **Many technical/proper nouns are conventionally left in English**, even in otherwise
- fully translated prose — e.g. in Spanish, Unity's own docs never translate `GameObject`,
- `Inspector`, `Prefab`, `Script`, or `Play Mode`; GitHub's Spanish glossary keeps `issue`
- and `pull request` as loanwords (GitHub's own UI ships English-only, so the loanword is
- literally what a user clicks). This repo's own code identifiers (`_ts.Memory`, `TsStart`,
- `TsBehaviour`, class/property names inside example code or referring to it) are never
- translated either — including inside descriptive text, e.g. `_ts.Name` must stay
- `_ts.Name`, not become `_ts.Nombre`.
- - **Other English phrasing has a real idiomatic equivalent that isn't the literal
- word-for-word choice** — e.g. "extends"/inheritance reads more naturally in Spanish as
- "hereda de" than a calque like "extiende"; Unity's Spanish manual calls `Instantiate()`
- "instanciar", not a generic "generar/crear". Prefer the term the target community
- actually uses in casual technical speech, not a dictionary's first hit.
- - Also double-check direction/meaning survived translation, not just word choice — a
- literal-sounding translation can silently invert an "A extends B" relationship if you're
- not reading the translated sentence back for what it actually asserts.
- - A word can be the "correct" dictionary or OS-level technical term and still not be what
- this content's actual audience says day to day — e.g. "arranque" is standard Spanish for
- booting an OS, but reads stiffer than "inicio"/"iniciar" for a script or behaviour starting
- up in casual creator-facing prose. When a general web search and a native speaker's ear
- disagree, defer to the native speaker: ask or take their correction over the search result.
-- **Worked example**: `docs/tsvrc/intro.md` has a real Spanish translation at
- `i18n/es/docusaurus-plugin-content-docs/current/tsvrc/intro.md`: same filename,
- same relative path, translated content. Copy that pattern for any other doc page.
- Verified in-browser: the translated page renders correctly, and links from it to
- still-untranslated pages fall back to English cleanly under the same `/es/` prefix.
-- **Headings that other pages link to by anchor** need an explicit, language-stable id,
- or translating the heading text breaks the anchor. Docusaurus's MDX v3 syntax for this
- is a comment, not `{#id}` (that's the old, now-invalid MDX v1/v2 form and fails the
- build): `## Play Mode tests: TsPlayModeTestBase {/* #play-mode-tests-tsplaymodetestbase */}`
- (see `docs/tsvrc/testing/testing-your-world.md`). Keep the same id across every
- language's version of that heading.
-
-## Commands
-
-- `npm start` — dev server at http://localhost:3000, hot-reloads content but not
- `docusaurus.config.ts` (restart the server after config changes).
-- `npm run build` — production build. Run this before calling a doc change done;
- `onBrokenLinks: 'throw'` means a broken link fails the build.
-
-## Cross-repo: tsvrc/tsvrc-core and tsvrc/docs
-
-This repo documents code that lives in the separate `tsvrc/tsvrc-core` repo. The two are not
-wired together technically — no submodule, no build-time fetch (see below for why). Nothing
-keeps them in sync automatically:
-
-- Changed public API, behavior, or the package description in `tsvrc/tsvrc-core`? Check whether
- `docs/tsvrc/` needs a matching update.
-- Changed something in `docs/tsvrc/`? Check it still matches what `tsvrc/tsvrc-core` actually
- does before publishing.
-
-A global hook flags this when either repo's docs-relevant files change. It's a nudge to go
-check, not proof the two are actually in sync.
-
-## Why there's no cross-repo build wiring
-
-We evaluated pulling `tsvrc/tsvrc-core`'s docs into this site via a git submodule at build time
-and rejected it. Surveying large community projects — Kubernetes, React, Vue, Python,
-Node.js, Docker — shows the dominant patterns are docs-in-the-code-repo (single product) or
-a fully self-contained docs repo, and for multi-product orgs, one consolidated repo
-namespaced by project (Docker moved to this after abandoning per-product federation). None
-of them merge separate repos into one build. Keep this repo self-contained unless a project
-genuinely outgrows it.
+@AGENTS.md
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index e1e2683..2ef27b8 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -13,22 +13,15 @@
This site follows the [Diátaxis framework](https://diataxis.fr/) (tutorial / how-to /
reference / explanation) and a house writing style aimed at not reading like AI-generated
-prose. Read these before writing or editing any page:
-
-- [`.claude/rules/doc-structure.md`](.claude/rules/doc-structure.md): which of the four
- categories a page belongs to, section conventions, linking instead of duplicating.
-- [`.claude/rules/writing-style.md`](.claude/rules/writing-style.md): words/phrases to
- avoid, sentence shape, formatting rules.
-
-Despite the `.claude/` path, both are written for any contributor, human or AI-assisted,
-not just for Claude specifically.
+prose. Read [STYLE_GUIDE.md](STYLE_GUIDE.md) before writing or editing any page: which of
+the four categories a page belongs to, section conventions, linking instead of
+duplicating, words/phrases to avoid, sentence shape, formatting rules.
## Multi-project layout
Each project under the `tsvrc` org gets its own `docs//` folder here rather than
-its own docs repo (see the root [`CLAUDE.md`](CLAUDE.md) for why). Adding docs for a new
-project means adding a folder here and a matching top-level category in `sidebars.ts`,
-not creating a new repo.
+its own docs repo. Adding docs for a new project means adding a folder here and a
+matching top-level category in `sidebars.ts`, not creating a new repo.
## Adding a translation
@@ -36,10 +29,24 @@ Docs content stays English-only and unversioned under `docs//`. A trans
lives in a parallel tree at
`i18n//docusaurus-plugin-content-docs/current//`, mirroring the same
relative paths and filenames; adding a language never touches the English files. See
-`docs/tsvrc/intro.md` and its Spanish counterpart for a worked example to copy. Before
-translating a technical term, check how the target language's own community or official
-docs (Unity's localized manual, GitHub's glossary) actually render it rather than
-translating literally.
+`docs/tsvrc/intro.md` and its Spanish counterpart for a worked example to copy.
+`src/pages/` translates differently: via `@docusaurus/Translate` in the component source,
+with strings in `i18n//code.json`.
+
+A heading another page links to by anchor needs a stable id that survives translation,
+since translating the heading text changes the anchor. Docusaurus's MDX v3 syntax for this
+is a comment, not `{#id}`: `## Play Mode tests {/* #play-mode-tests */}`. Sidebar category
+labels live in `sidebars.ts` itself, not a translatable file, so they render in English for
+every locale regardless of translated content underneath. Don't override Docusaurus's
+theme chrome (pagination, admonition labels, sidebar buttons) in this site's own
+`code.json`; it ships its own translated defaults per locale.
+
+Before translating a technical term, check how the target language's own community or
+official docs (Unity's localized manual, GitHub's glossary, VRChat's community) actually
+render it rather than translating literally. Some terms stay in English even in translated
+prose, others have a real idiomatic equivalent that isn't the literal word-for-word choice.
+This repo's own code identifiers (`_ts.Memory`, `TsStart`) are never translated, including
+inside prose.
## Submitting a pull request
diff --git a/README.md b/README.md
index b599a36..1f6cc91 100644
--- a/README.md
+++ b/README.md
@@ -1,57 +1,39 @@
-# Website
+# TsVRC docs
-This website is built using [Docusaurus](https://docusaurus.io/), a modern static website generator.
+[](https://github.com/tsvrc/docs/actions/workflows/deploy.yml)
+[](LICENSE)
-## License
-
-This repository, including the Docusaurus site's own source code, is licensed under
-[CC BY 4.0](LICENSE). Code samples embedded in a project's doc pages (under
-`docs//`) that are drawn directly from that project's own repository keep that
-project's license, not this repository's, since they originate there. Check the linked
-project's own `LICENSE` for the specific terms.
-
-The TsVRC name and logo are governed separately, see
-[TRADEMARK.md](https://github.com/tsvrc/.github/blob/main/TRADEMARK.md).
+The [Docusaurus](https://docusaurus.io/) site behind [tsvrc.com](https://tsvrc.com),
+documenting every project under the `tsvrc` GitHub org. Framework code lives in
+[`tsvrc/tsvrc-core`](https://github.com/tsvrc/tsvrc-core); this repo is docs and content
+only.
-## Installation
+## Development
```bash
npm install
+npm start
```
-**Note**: feel free to use the package manager of your choice.
-
-## Local Development
-
-```bash
-npm run start
-```
-
-This command starts a local development server and opens up a browser window. Most changes are reflected live without having to restart the server.
-
-## Build
+`npm start` runs a hot-reloading dev server at `http://localhost:3000`. Content changes
+reload live; changes to `docusaurus.config.ts` need a server restart.
```bash
npm run build
```
-This command generates static content into the `build` directory and can be served using any static contents hosting service.
+Builds the static site into `build/`. `onBrokenLinks: 'throw'` means a broken internal
+link fails this command, so run it before calling a doc change done.
## Deployment
-Using SSH:
+Every push to `main` builds and deploys to GitHub Pages automatically via
+[`.github/workflows/deploy.yml`](.github/workflows/deploy.yml). There's no manual deploy
+step.
-```bash
-USE_SSH=true npm run deploy
-```
-
-Not using SSH:
-
-```bash
-GIT_USER= npm run deploy
-```
+## Support
-If you are using GitHub Pages for hosting, this command is a convenient way to build the website and push to the `gh-pages` branch.
+Support TsVRC at [tsvrc.com/support](https://tsvrc.com/support).
## Contributing
@@ -61,3 +43,13 @@ layout, and how to submit a change. Community conduct is covered by the
and [GOVERNANCE.md](https://github.com/tsvrc/.github/blob/main/GOVERNANCE.md) covers how
decisions get made. Found a security issue? See [SECURITY.md](SECURITY.md) rather than
opening a public issue.
+
+## License
+
+This repository is licensed under [CC BY 4.0](LICENSE). Code samples embedded in a
+project's doc pages (under `docs//`) that are drawn directly from that project's
+own repository keep that project's license, not this repository's, since they originate
+there. Check the linked project's own `LICENSE` for the specific terms.
+
+The TsVRC name and logo are governed separately, see
+[TRADEMARK.md](https://github.com/tsvrc/.github/blob/main/TRADEMARK.md).
diff --git a/STYLE_GUIDE.md b/STYLE_GUIDE.md
new file mode 100644
index 0000000..63c6f76
--- /dev/null
+++ b/STYLE_GUIDE.md
@@ -0,0 +1,107 @@
+# Style guide
+
+Conventions for any doc content in this repo, human-written or AI-assisted. Applies to
+every `.md`/`.mdx` file under `docs/`, `i18n/`, and `src/pages/`.
+
+## Doc structure
+
+### Pick the right category (Diátaxis)
+
+Every page does exactly one of these jobs. Mixing them inside one page is why docs end up
+feeling unfocused:
+
+- **Tutorial** — a guided first experience. Optimizes for the reader finishing
+ successfully, not for completeness.
+- **How-to guide** — solves one specific, named problem for someone who already knows the
+ basics. Skips background explanation.
+- **Reference** — describes the thing accurately and completely (an API, a config schema,
+ a component's props). No opinions, no narrative, facts kept accurate as the thing
+ changes.
+- **Explanation** — the why: design rationale, tradeoffs, how pieces fit together. The only
+ category where discussing alternatives and context belongs.
+
+Not sure which one a page is? That's the sign it's trying to do two jobs, split it.
+
+### Fully documenting a component: don't skip details
+
+When writing reference or explanation content for a package or component, cover what a
+careful reviewer would ask about, not just the happy path:
+
+- What it's for, in one sentence a newcomer understands without prior context.
+- Its public surface: what a consumer actually calls, imports, or configures.
+- Invariants and constraints: what must be true before and after, what it assumes the
+ caller already did.
+- Failure modes: what happens when it's misused, and what signals that (error, log,
+ silent no-op).
+- Edge cases that aren't obvious from the signature: ordering, concurrency, lifecycle,
+ platform-specific behavior.
+- Why it's built this way, when the reason isn't obvious. Link to an explanation page
+ rather than re-explaining inline.
+
+Missing one of these isn't a style problem, it's a coverage gap: the kind of thing a
+reader hits in production with nowhere to look it up.
+
+### Sections and metadata
+
+- One H1 per page, matching the frontmatter title.
+- A one- or two-sentence summary right under the H1: what this page is and who it's for,
+ before anything else.
+- Sentence-case headings throughout (see Writing style below).
+
+### Linking instead of duplicating
+
+- Never restate content that already exists on another page. Link to the canonical page
+ instead: two copies of the same explanation will drift, a link can't.
+- Link relatively within the same project folder (`../other-page`); link from the docs
+ root when crossing into another project's docs (`/docs//...`).
+- When a decision has real tradeoffs and a future reader will reasonably ask "why was it
+ done this way", record it once as a short decision note (what, why, alternatives
+ considered) and link to it from everywhere it's relevant. Don't re-litigate it inline on
+ every page that touches it.
+
+## Writing style: sound human, say only what's needed
+
+Docs written by an LLM have a recognizable smell, and readers trust content less once they
+notice it. Avoid it.
+
+### Cut, don't pad
+
+- If a reader can see it by reading the code, don't restate it here. A doc earns its place
+ by adding what code can't show: why, tradeoffs, what breaks, what to do next.
+- Before adding a paragraph, check what it adds beyond the paragraph before it or the code
+ itself. If nothing, delete it.
+- One idea per section. If two sections could swap order without anything breaking, that's
+ a list wearing the structure of an argument, tighten it.
+
+### Words and phrases to avoid
+
+These are statistically over-represented in LLM output and read as filler to anyone who has
+seen a lot of AI-generated text: delve, leverage (verb), robust, seamless, comprehensive,
+cutting-edge, pivotal, meticulous, landscape / realm / tapestry (as metaphors), utilize,
+"in order to", streamline, foster, elevate, unlock, harness, ecosystem (unless it's
+literally an ecosystem), myriad, plethora, crucial, paramount, nuanced, "it's worth noting",
+"it's important to note", "at its core", "best practices" (name the actual practice instead).
+
+Hedging kills technical writing: cut "could potentially", "may eventually", "it's worth
+mentioning". State the fact or the tradeoff directly.
+
+### Sentence and paragraph shape
+
+- Vary sentence length on purpose. A row of same-length sentences reads like a template.
+- Don't open with "In today's...", "Let's explore...", or "In this doc, we will...". Start
+ with the actual point.
+- Avoid the "It's not X, it's Y" construction. State the positive claim.
+- Sentence case for headings ("Set up your environment"), not title case
+ ("Set Up Your Environment").
+- Skip chatbot artifacts entirely: "I hope this helps", "Great question", "Feel free to
+ reach out". This is documentation, not a chat reply.
+
+### Punctuation and formatting
+
+- Avoid em dashes for asides in prose; use a comma, a colon, or a full stop instead. (An em
+ dash inside a bolded-term list item is fine: `- **Term** — one-line description`.)
+- Don't bold more than one phrase per section, bolding everything bolds nothing.
+- Reach for prose over bullets when explaining an idea with real connections between its
+ parts. Bullets suit genuinely parallel, independent items: flags, a checklist, install
+ steps. A bulleted list of bare noun phrases ("Fast. Reliable. Secure.") explains nothing;
+ say what's actually fast, reliable, or secure, and why.