diff --git a/docusaurus.config.ts b/docusaurus.config.ts index 885e111..dcbde74 100644 --- a/docusaurus.config.ts +++ b/docusaurus.config.ts @@ -92,8 +92,21 @@ const config: Config = { // versions exist yet. When the first is cut // (`npm run docusaurus docs:version 0.7`), it becomes the default // "latest" and "dev" stays as the work-in-progress version. + // IMPORTANT: providing `exclude` REPLACES Docusaurus's defaults rather + // than adding to them, so the defaults are repeated here verbatim. The + // '**/_*/**' entry is what keeps docs/_internal/** — the team's plans, + // research, retrospectives and QA notes — out of the build. Dropping it + // publishes those notes and fails the build on their repo-relative + // image links. exclude: [ - // Internal working notes — not part of the published site. + // --- Docusaurus defaults. Do not remove. --- + '**/_*.{js,jsx,ts,tsx,md,mdx}', + '**/_*/**', + '**/*.test.{js,jsx,ts,tsx}', + '**/__tests__/**', + // --- Legacy locations, pre-restructure. Harmless once upstream has + // moved this content under docs/_internal/; kept so this config is + // correct whichever order the two PRs land in. --- 'superpowers/**', 'authbridge/**', 'automation-health.md', @@ -110,6 +123,10 @@ const config: Config = { v, { label: v === LATEST_VERSION ? `v${v} (latest)` : `v${v}`, + // Docusaurus already routes the lastVersion at the bare + // /docs and the rest under /docs/. Stating it + // here is deliberate: it documents the URL shape at the + // point a reader looks for it. Keep it. path: v === LATEST_VERSION ? '' : v, badge: true, }, diff --git a/ecosystem/welcome.mdx b/ecosystem/welcome.mdx index fa5dd88..e991585 100644 --- a/ecosystem/welcome.mdx +++ b/ecosystem/welcome.mdx @@ -116,7 +116,7 @@ and built on open standards, supporting A2A and MCP.
{/* Primary CTAs point at the project repos (docs hidden pre-launch). */} - Get started + Get started
diff --git a/package-lock.json b/package-lock.json index 2381394..1d24e6d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,6 +11,7 @@ "@docusaurus/core": "3.10.1", "@docusaurus/preset-classic": "3.10.1", "@docusaurus/theme-mermaid": "3.10.1", + "@fontsource/ibm-plex-mono": "^5.3.0", "@fontsource/ibm-plex-sans": "^5.2.8", "@mdx-js/react": "^3.0.0", "clsx": "^2.0.0", @@ -3945,6 +3946,14 @@ "node": ">=20.0" } }, + "node_modules/@fontsource/ibm-plex-mono": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/@fontsource/ibm-plex-mono/-/ibm-plex-mono-5.3.0.tgz", + "integrity": "sha512-eTgnZjZEGk1QtD3ZstF+Vclo2HLAni8YMy34/DxllwZvyz1lR/1RF/xTiAquOBO7MvqBx8D2Ig2WCPMVfdZu7Q==", + "funding": { + "url": "https://github.com/sponsors/ayuhito" + } + }, "node_modules/@fontsource/ibm-plex-sans": { "version": "5.2.8", "resolved": "https://registry.npmjs.org/@fontsource/ibm-plex-sans/-/ibm-plex-sans-5.2.8.tgz", diff --git a/package.json b/package.json index 5030d53..b161631 100644 --- a/package.json +++ b/package.json @@ -20,6 +20,7 @@ "@docusaurus/core": "3.10.1", "@docusaurus/preset-classic": "3.10.1", "@docusaurus/theme-mermaid": "3.10.1", + "@fontsource/ibm-plex-mono": "^5.3.0", "@fontsource/ibm-plex-sans": "^5.2.8", "@mdx-js/react": "^3.0.0", "clsx": "^2.0.0", diff --git a/scripts/sync-docs.sh b/scripts/sync-docs.sh index cc24c5a..0a3df3e 100755 --- a/scripts/sync-docs.sh +++ b/scripts/sync-docs.sh @@ -43,22 +43,30 @@ fi UP="$SRC/$SUBDIR" if [[ -d "$UP" ]]; then mkdir -p "$DEST" - rsync -a --delete --exclude '.DS_Store' "$UP"/ "$DEST"/ + # _internal/ holds the team's engineering notes (plans, research, retrospectives, + # QA matrices, developer setup). Docusaurus would ignore them anyway via its + # default '**/_*/**' exclude, but keeping them out of the site tree entirely + # matters for versioning: `docusaurus docs:version` snapshots whatever is in + # docs/ into a COMMITTED versioned_docs/ folder, so without this every release + # would freeze a copy of those notes into this repo. + # '/_internal' is anchored to the top of the transfer, so it excludes exactly + # docs/_internal/ and not a directory of that name at any other depth. + rsync -a --delete --exclude '.DS_Store' --exclude '/_internal' "$UP"/ "$DEST"/ - # The /docs/ root is a GENERATED INDEX (see sidebars.ts), not a markdown file. - # By default Docusaurus maps docs/README.md to the /docs/ route, which would - # collide with that generated index. Give README a slug so it ships as an - # ordinary page (/docs/readme) and frees the root route. Prepend frontmatter - # (upstream README has none). Skip if it somehow already has frontmatter. - README="$DEST/README.md" - if [[ -f "$README" ]] && ! head -1 "$README" | grep -q '^---$'; then - printf -- '---\nslug: /readme\nsidebar_label: Overview\n---\n\n%s' "$(cat "$README")" > "$README.tmp" - mv "$README.tmp" "$README" + # MIGRATION WINDOW: the restructured docs tree provides docs/index.md with + # `slug: /` as the /docs/ landing page (see sidebars.ts, which no longer wraps + # everything in a generated-index category). Until that lands upstream, + # synthesise a minimal one so the /docs/ route — and the navbar's + # "Documentation" link — still resolves. This becomes a no-op the moment + # upstream ships its own index.md, and can then be deleted. + if [[ ! -f "$DEST/index.md" ]]; then + printf -- '---\ntitle: Rossoctl documentation\nslug: /\n---\n\nChoose a section from the sidebar.\n' \ + > "$DEST/index.md" + echo "==> synthesised docs/index.md (upstream does not provide one yet)." fi - # Rewrite links to README.md -> index.md (Docusaurus folder-index convention). - # (README is no longer the folder index, but existing ./README.md links across - # the docs still resolve to the same page; keep this so cross-links don't break.) + # Rewrite any remaining links to README.md -> index.md (Docusaurus folder-index + # convention), so a cross-link written against a folder README still resolves. find "$DEST" -name '*.md' -type f -print0 | while IFS= read -r -d '' f; do sed -i.bak -E 's#\]\(([^)]*)README\.md#](\1index.md#g' "$f" && rm -f "$f.bak" done diff --git a/sidebars.ts b/sidebars.ts index c46ae94..006bae9 100644 --- a/sidebars.ts +++ b/sidebars.ts @@ -3,34 +3,20 @@ import type {SidebarsConfig} from '@docusaurus/plugin-content-docs'; /** * The docs sidebar is fully AUTOGENERATED from the docs/ folder tree, so the * left nav mirrors the folder structure 1:1. Content authors control ordering - * and labels with: + * and labels upstream in rossoctl/rossoctl:docs/ with: * - _category_.json (per folder: label, position, link, collapsed) * - frontmatter (per file: sidebar_position, sidebar_label, title) * - * There is nothing to edit in this file when adding/removing/reordering docs. - * The key here ("docsSidebar") must match `sidebarId` in docusaurus.config.ts. + * There is nothing to edit in this file when adding, removing, or reordering + * docs. The key here ("docsSidebar") must match `sidebarId` in + * docusaurus.config.ts. + * + * The /docs/ route is served by docs/index.md, which carries `slug: /`. There is + * deliberately no wrapping category: one would add a redundant "Documentation" + * level above the nine real sections. */ const sidebars: SidebarsConfig = { - // The docs root (/docs/) is a GENERATED INDEX — an auto-built list of the - // sections below — rather than a single markdown file. This is durable: the - // docs/ folder is regenerated from upstream on every build (scripts/sync-docs.sh), - // so we don't rely on any specific file (like README.md) landing at the root. - // README.md still ships as a normal page; sync-docs.sh gives it a slug so it - // no longer claims the /docs/ route. - docsSidebar: [ - { - type: 'category', - label: 'Documentation', - link: { - type: 'generated-index', - title: 'Rossoctl Documentation', - description: - 'Guides, concepts, and references for deploying and operating Rossoctl.', - slug: '/', - }, - items: [{type: 'autogenerated', dirName: '.'}], - }, - ], + docsSidebar: [{type: 'autogenerated', dirName: '.'}], }; export default sidebars; diff --git a/src/css/custom.css b/src/css/custom.css index 4cff44e..714176b 100644 --- a/src/css/custom.css +++ b/src/css/custom.css @@ -10,6 +10,10 @@ @import '@fontsource/ibm-plex-sans/500.css'; @import '@fontsource/ibm-plex-sans/600.css'; @import '@fontsource/ibm-plex-sans/700.css'; +/* IBM Plex Mono for code — the sibling of Plex Sans, so code and prose share + the same design language and metrics. */ +@import '@fontsource/ibm-plex-mono/400.css'; +@import '@fontsource/ibm-plex-mono/500.css'; /* ============================================================================ * DESIGN TOKENS (from the designer's spec). Applied site-wide via Infima vars. @@ -383,3 +387,317 @@ h6 { [data-theme='dark'] .rosso-footer__copyright { color: #000000; } + +/* ============================================================================ + * DOCUMENTATION READING TYPOGRAPHY + * + * Scoped to the documentation and contributing instances only, via the + * plugin-id class Docusaurus puts on . The ecosystem landing page + * (plugin-id-ecosystem) keeps the marketing type scale above — its 3rem hero + * is deliberate and is not touched here. + * + * Why: the site-wide scale is tuned for a landing page. On a long reference + * page it read as bulky, because h1 was 3rem against 0.875rem body — a 3.4x + * jump — and h3 through h6 were all 1.5rem, so nested sections had no + * hierarchy at all. This lowers the top of the scale, raises the body, and + * differentiates h3/h4, giving a 2.25x jump instead. + * + * Everything here is plain Infima/Docusaurus theming — stable theme classes + * and CSS variables, no swizzling and no hashed class names. + * ==========================================================================*/ + +:root { + --rosso-doc-measure: 46rem; /* ~72 characters at 1rem */ + --rosso-doc-lead-color: #454545; + --rosso-doc-rule: #e0e0e0; + --rosso-code-bg: #f4f4f4; + --rosso-table-head-bg: #f4f4f4; + --rosso-sidebar-label: #6f6f6f; +} + +[data-theme='dark'] { + --rosso-doc-lead-color: #c6c6c6; + --rosso-doc-rule: #383838; + --rosso-code-bg: #161616; + --rosso-table-head-bg: #1c1c1c; + --rosso-sidebar-label: #a8a8a8; +} + +/* ---------- Body ---------- */ + +.plugin-id-default .markdown, +.plugin-id-contributing .markdown { + font-size: 1rem; + line-height: 1.7; + max-width: var(--rosso-doc-measure); +} + +/* Tables, code and diagrams are allowed to use the full column width. */ +.plugin-id-default .markdown > table, +.plugin-id-default .markdown > .theme-code-block, +.plugin-id-default .markdown > p > img, +.plugin-id-default .markdown > .docusaurus-mermaid-container, +.plugin-id-contributing .markdown > table, +.plugin-id-contributing .markdown > .theme-code-block { + max-width: none; +} + +.plugin-id-default .markdown p, +.plugin-id-contributing .markdown p { + margin-bottom: 1.1rem; +} + +/* ---------- Headings ---------- */ + +.plugin-id-default .markdown h1, +.plugin-id-contributing .markdown h1 { + font-size: 2.25rem; + font-weight: 500; + line-height: 1.15; + letter-spacing: -0.02em; + margin-bottom: 0.75rem; +} + +/* The first paragraph after the title is the page's standfirst. Every docs page + opens with a one- or two-sentence orientation, so give it slightly larger, + quieter type — it separates "what this page is" from the body. */ +.plugin-id-default .markdown > header + p, +.plugin-id-contributing .markdown > header + p { + font-size: 1.1875rem; + line-height: 1.6; + color: var(--rosso-doc-lead-color); + margin-bottom: 2rem; +} + +/* h2 opens a section, so it gets air above and a hairline rule under it. The + rule is what lets you find section boundaries while scrolling, and is the + main reason long pages stop reading as one undifferentiated block. */ +.plugin-id-default .markdown h2, +.plugin-id-contributing .markdown h2 { + font-size: 1.5rem; + font-weight: 600; + line-height: 1.3; + letter-spacing: -0.01em; + margin-top: 3rem; + margin-bottom: 1.25rem; + padding-bottom: 0.5rem; + border-bottom: 1px solid var(--rosso-doc-rule); +} + +.plugin-id-default .markdown h3, +.plugin-id-contributing .markdown h3 { + font-size: 1.1875rem; + font-weight: 600; + line-height: 1.4; + margin-top: 2.25rem; + margin-bottom: 0.65rem; +} + +.plugin-id-default .markdown h4, +.plugin-id-contributing .markdown h4 { + font-size: 1rem; + font-weight: 600; + line-height: 1.45; + margin-top: 1.75rem; + margin-bottom: 0.5rem; +} + +.plugin-id-default .markdown h5, +.plugin-id-default .markdown h6, +.plugin-id-contributing .markdown h5, +.plugin-id-contributing .markdown h6 { + font-size: 0.9375rem; + font-weight: 600; + line-height: 1.45; + margin-top: 1.5rem; + margin-bottom: 0.4rem; +} + +/* A heading immediately after the title should not add its usual top margin. */ +.plugin-id-default .markdown > header + h2, +.plugin-id-contributing .markdown > header + h2 { + margin-top: 1.5rem; +} + +/* ---------- Lists ---------- */ + +.plugin-id-default .markdown ul, +.plugin-id-default .markdown ol, +.plugin-id-contributing .markdown ul, +.plugin-id-contributing .markdown ol { + padding-left: 1.35rem; + margin-bottom: 1.1rem; +} + +.plugin-id-default .markdown li, +.plugin-id-contributing .markdown li { + margin-bottom: 0.4rem; +} + +.plugin-id-default .markdown li > ul, +.plugin-id-default .markdown li > ol { + margin-top: 0.4rem; + margin-bottom: 0.4rem; +} + +/* ---------- Links ---------- */ + +/* Brand red carries a lot of weight in running text. Underlining it means a + link reads as a link rather than as emphasis. */ +.plugin-id-default .markdown p > a, +.plugin-id-default .markdown li > a, +.plugin-id-default .markdown td > a, +.plugin-id-contributing .markdown p > a, +.plugin-id-contributing .markdown li > a { + text-decoration: underline; + text-decoration-thickness: 1px; + text-underline-offset: 0.15em; +} + +/* ---------- Code ---------- */ + +.plugin-id-default .markdown code, +.plugin-id-contributing .markdown code { + font-family: 'IBM Plex Mono', ui-monospace, 'SFMono-Regular', Consolas, + monospace; + font-size: 0.875em; +} + +/* Inline code: a quiet tint, no border — a border on every symbol name makes a + reference page look like a form. */ +.plugin-id-default .markdown :not(pre) > code, +.plugin-id-contributing .markdown :not(pre) > code { + background: var(--rosso-code-bg); + border: none; + border-radius: 3px; + padding: 0.12em 0.34em; + color: inherit; +} + +.plugin-id-default .theme-code-block, +.plugin-id-contributing .theme-code-block { + --prism-background-color: var(--rosso-code-bg) !important; + box-shadow: none; + border: 1px solid var(--rosso-doc-rule); + border-radius: 4px; + margin-bottom: 1.5rem; +} + +.plugin-id-default .theme-code-block pre code, +.plugin-id-contributing .theme-code-block pre code { + font-size: 0.875rem; + line-height: 1.65; +} + +/* ---------- Tables ---------- */ + +.plugin-id-default .markdown table, +.plugin-id-contributing .markdown table { + display: table; + width: 100%; + border-collapse: collapse; + border: 1px solid var(--rosso-doc-rule); + font-size: 0.9375rem; + line-height: 1.55; + margin-bottom: 1.75rem; +} + +.plugin-id-default .markdown table thead tr, +.plugin-id-contributing .markdown table thead tr { + background: var(--rosso-table-head-bg); + border-bottom: 1px solid var(--rosso-doc-rule); +} + +.plugin-id-default .markdown table th, +.plugin-id-contributing .markdown table th { + font-weight: 600; + text-align: left; + padding: 0.7rem 0.9rem; +} + +.plugin-id-default .markdown table td, +.plugin-id-contributing .markdown table td { + padding: 0.7rem 0.9rem; + border-top: 1px solid var(--rosso-doc-rule); +} + +/* Infima stripes alternate rows; hairlines read cleaner on a reference table. */ +.plugin-id-default .markdown table tr:nth-child(2n), +.plugin-id-contributing .markdown table tr:nth-child(2n) { + background: transparent; +} + +/* ---------- Admonitions ---------- */ + +.plugin-id-default .theme-admonition, +.plugin-id-contributing .theme-admonition { + border-radius: 4px; + border-left-width: 3px; + font-size: 0.9375rem; + margin-bottom: 1.5rem; +} + +.plugin-id-default .theme-admonition p:last-child { + margin-bottom: 0; +} + +/* ---------- Sidebar ---------- */ + +.plugin-id-default .theme-doc-sidebar-menu { + font-size: 0.9375rem; +} + +/* Top-level section headings read as group labels, the way they do in a + reference manual: small, letter-spaced, quiet. They stay clickable. */ +.plugin-id-default + .theme-doc-sidebar-menu + > .theme-doc-sidebar-item-category-level-1 + > .menu__list-item-collapsible + > .menu__link { + font-size: 0.75rem; + font-weight: 600; + letter-spacing: 0.06em; + text-transform: uppercase; + color: var(--rosso-sidebar-label); +} + +.plugin-id-default + .theme-doc-sidebar-menu + > .theme-doc-sidebar-item-category-level-1 { + margin-top: 1.25rem; +} + +.plugin-id-default .menu__link { + line-height: 1.45; +} + +/* ---------- Table of contents ---------- */ + +.plugin-id-default .table-of-contents { + font-size: 0.8125rem; + line-height: 1.5; + border-left: none; +} + +.plugin-id-default .table-of-contents__left-border { + border-left: none; +} + +.plugin-id-default .theme-doc-toc-desktop > .table-of-contents::before { + content: 'On this page'; + display: block; + font-size: 0.6875rem; + font-weight: 600; + letter-spacing: 0.08em; + text-transform: uppercase; + color: var(--rosso-sidebar-label); + padding: 0 0 0.6rem 0.6rem; +} + +.plugin-id-default .table-of-contents__link { + color: var(--rosso-sidebar-label); +} + +.plugin-id-default .table-of-contents__link--active { + font-weight: 500; +}