Skip to content

About

hyperverse.world — documentation and showcase for the hyperverse R ecosystem

Topics

Resources

Stars

2 stars

Watchers

0 watching

Forks

Repository files navigation

hyperverse.world

Source for hyperverse.world, the documentation and showcase site for the hyperverse R ecosystem.

Two tools, deliberately:

  • Astro builds the site. Every page but the home page is static HTML plus a few inline scripts (theme toggle, hero canvas, copy buttons) and ships no framework runtime at all. The home page hydrates one component, the design system's live htmxr terminal, on Preact: about 19 KB gzipped.
  • Quarto builds the talk decks, and only those. revealjs (speaker notes, fragments, r-fit-text, self-contained output) has no equivalent worth having on the Astro side.

Deployed to GitHub Pages on every push to main.


Requirements

Node 22 or newer
Quarto 1.9.37 (pinned in CI, see .github/workflows/publish.yml)

R is not required. _data/*.yml is read directly by the Astro content loader, and no deck contains an R chunk.

Local development

npm install

npm run talks    # render the decks into public/talks/ (needed once)
npm run dev      # http://localhost:4321, with hot reload

npm run talks is only needed the first time, and afterwards whenever you edit a deck. Rendering the three decks takes about 10 seconds, which is why it is a separate command rather than part of npm run dev.

Command What it does
npm run dev Dev server with hot reload
npm run talks Render the Quarto decks into public/talks/
npm run build Build the site into dist/ (decks must already be rendered)
npm run build:all talks then build, what CI runs
npm run preview Serve dist/ exactly as it will be deployed
npm run check Type-check .astro and .ts files

Editing a .qmd deck updates the talks listing live in npm run dev, and so does editing _data/*.yml: the content loaders watch both. The deck itself only changes after npm run talks, since that is Quarto's job.

Structure

src/
├── pages/                  # routes
│   ├── index.astro         # home: hero + featured packages
│   ├── ecosystem.astro     # package grids, from _data/*.yml
│   ├── why.mdx             # coming from Shiny
│   ├── getting-started.mdx
│   ├── design-system.astro # token & component showcase
│   ├── talks/index.astro   # listing, built from the decks' frontmatter
│   ├── guides/             # listing + one route per guide
│   └── blog/               # listing + one route per post
├── content/                # the prose those routes render
│   ├── guides/*.mdx
│   └── blog/*.mdx
├── layouts/
│   ├── BaseLayout.astro    # <head>, nav, footer, theme bootstrap
│   └── DocLayout.astro     # prose + table of contents with reading progress
├── components/
│   ├── ds/                 # copied verbatim from the design system, see its README
│   └── *.astro             # the site layer
├── content.config.ts       # loaders: talks, guides, blog, packages, partners
├── styles/
│   ├── tokens/*.css        # the design system's tokens, verbatim
│   └── global.css          # base + prose layer
├── scripts/                # shared behaviour (tab panels)
└── lib/                    # rehype plugins

talks/                      # Quarto revealjs decks (its own Quarto project)
├── _quarto.yml             # renders into ../public/talks/
└── <slug>/index.qmd

_data/                      # packages.yml, partners.yml
assets/                     # hyperverse-revealjs.scss, used by the decks only
public/                     # static assets + the rendered decks (gitignored)

Writing content

Where a piece of writing goes

Four kinds of content, told apart by what they are for and where they live.

Kind Lives in Route Sorted by For
Navigation page src/pages/*.mdx or .astro one per file n/a The structure of the site: why, get started, ecosystem. Rarely added.
Guide src/content/guides/*.mdx /guides/<file> order A task, once you have something running. Revised over time, so it carries no date.
Post src/content/blog/*.mdx /blog/<file> date Dated writing: a release, a decision, a lesson. Stays as written.
Post published elsewhere src/content/blog/*.mdx with external none date Written for ThinkR or anywhere else. Listed here, rendered there.

The last one is the reason posts and guides are separate collections rather than one: an external post is still a post, it just points somewhere else.

Adding external: { url, publisher } changes four things at once, all in the same direction: the listing and the feed link to the original, no local route is generated so nothing competes with it in search results, and llms.txt names the publisher so a model quoting the piece credits the right one.

A guide or a post

Both are MDX in src/content/, and the route is generated for you. A guide goes in guides/, a post in blog/; the filename becomes the URL.

---
title: "Monitoring errors in production"
description: "One sentence. Shown under the title, in the listing, and in meta tags."
order: 10          # guides only: how the listing is sorted
date: 2026-09-16   # posts only
draft: false       # true hides it from routes, listings and the sitemap
tags: [glitchtipr]
---

import Callout from '../../components/Callout.astro';

Markdown as usual. Separate sections with `##`; do not add rules of your own,
the prose layer deliberately draws none.

src/content/blog/writing-a-post.mdx is a draft template covering the rest.

A section only appears in the navbar once it has something published, so an empty Blog is never advertised.

A standalone page

For something that is not part of a section, add an .mdx file directly to src/pages/ with layout: ../layouts/DocLayout.astro in its frontmatter, then link it: the navbar (links in src/components/Nav.astro) is for pages a visitor needs, the footer columns (columns in src/components/Footer.astro) for everything else. /talks and /design-system live in the footer for that reason.

A talk

Create talks/<slug>/index.qmd with the usual frontmatter:

---
title: "Hyperverse"
subtitle: "..."
author: "Arthur Bréant"
date: 2026-06-17
event: "Rencontres R 2026 à Nantes"
event-en: "Rencontres R 2026 in Nantes, France"
description: "..."
description-en: "..."
categories: [htmxr, plumber2]
langs: [fr, en]
image: img/cover.png
---

Then npm run talks. The listing at /talks reads this frontmatter directly, so there is no second place to update.

For a translation, add index-<lang>.qmd in the same folder and list the language in langs. The loader checks which files actually exist on disk, so the listing can never link to a deck that was not rendered.

A package

Edit _data/packages.yml (or partners.yml for third-party packages):

- name: mypackage
  desc: "Short description."
  badge: In development
  badge_class: badge-dev
  github: https://github.com/hyperverse-r/mypackage
  pkgdown: ~

badge_class accepts badge-cran, badge-dev or badge-planned. Cards render in file order, so put the important ones first. Use ~ for "no link".

Fonts

Self-hosted in public/fonts/, declared in src/styles/tokens/fonts.css. This is the one file that deliberately diverges from the design system, which uses an @import from Google Fonts: right for a design canvas, wrong for a site, since an @import inside a stylesheet cannot start until that stylesheet has parsed.

Only weights that exist and are used. Lato has no 500 or 600 (the family is 100/300/400/700/900), so asking for them fetched nothing.

Watch what a page actually downloads before adding a weight. A single <b> in the hero was pulling 30 KB of JetBrains Mono 700 for two digits; giving it the 600 already on the page removed a whole file from every visit.

To regenerate after changing a family or weight:

# 1. ask Google for the CSS as a modern browser would see it
curl -s -A 'Mozilla/5.0 ... Chrome/120' \
  'https://fonts.googleapis.com/css2?family=Lato:wght@400;700&display=swap' > /tmp/gf.css
# 2. download each latin / latin-ext woff2 it lists into public/fonts/
# 3. copy its @font-face blocks into src/styles/tokens/fonts.css, swapping the
#    remote src for /fonts/<file>.woff2 and keeping the unicode-range

Keep font-display: swap and the unicode-range: the range is what stops a visitor reading English from ever fetching the latin-ext file.

Discoverability

Every page carries a canonical URL, a description, Open Graph and Twitter cards pointing at /og.png, and JSON-LD. Guides and posts declare themselves as TechArticle; everything else is the site's WebSite schema, which also describes hyperverse as SoftwareSourceCode so a search engine can tell what the project is.

Endpoint What it is
/sitemap-index.xml Generated. Drafts and redirects are excluded.
/robots.txt Points at the sitemap; keeps crawlers out of the multi-megabyte decks.
/rss.xml Blog posts only. A guide is revised, not announced.
/llms.txt A plain-text map of the site for language models, per llmstxt.org.
/llms-full.txt Every page as one plain-text document, MDX components unwrapped.

The last three are generated from the same collections the site renders, so they cannot drift from it. /og.png is the exception: it is a committed image, built from an HTML card. Re-shoot it if the wordmark or the tagline ever changes.

Design system

The visual language comes from a Claude Design project.

src/styles/tokens/ and src/components/ds/ are copied from it unmodified: do not hand-edit them, change the design system and re-sync. Everything else in src/components/ is the site layer, written against those tokens.

Every colour, space, radius and duration is a --hv-* custom property. Dark is the default; [data-theme="light"] flips it. /design-system renders the whole set live, so it cannot drift from the tokens.

The components are authored against React; the site runs Preact in compat mode, so they work unchanged on a much smaller runtime. Only the hero is hydrated. Everything else in src/components/ is a .astro port that emits the same DOM with CSS instead of component state. See src/components/ds/README.md for which is which, and why.

Two rules worth knowing before touching the styles:

  • Vampiro One is for the wordmark and the hero h1 only. Everything else, h2 included, is Lato.
  • Code panels never flip to light. They use the always-dark --hv-code-* family in both themes, because code reads better on a dark ground.
  • The glow has no light-theme value upstream. HeroBackdrop.astro defines its own, because the design system's pale violet at 0.06–0.10 alpha is invisible on #f8f8f8.

public/

Copied into the build untouched, and therefore served: anything placed here is public, including notes. hyperverse.svg, assets/, og.png, robots.txt and CNAME live there; talks/ is written by npm run talks and is gitignored.

Never put a redirect file here for a URL whose stem matches a real route. A file named talks.html shadows the /talks route on any host that resolves /talks to talks.html, and a redirect back to /talks then loops forever. Old URLs are handled by redirects in astro.config.mjs.

Deployment

.github/workflows/publish.yml renders the decks, builds the site and pushes dist/ to the gh-pages branch. The custom domain comes from public/CNAME.

Deck output is byte-for-byte reproducible for a given Quarto version, so gh-pages does not grow when nothing changed, which is why the Quarto version is pinned in the workflow rather than floating.

About

hyperverse.world — documentation and showcase for the hyperverse R ecosystem

Topics

Resources

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages