Skip to content

feat: build the Index, the Project pages and their og:images - #2

Merged
rqbazan merged 18 commits into
mainfrom
feat/site
Oct 17, 2026
Merged

rqbazan merged 18 commits into
mainfrom
feat/site

Conversation

@rqbazan

@rqbazan rqbazan commented Oct 17, 2026 •

Copy link
Copy Markdown
Member

Summary

lab.variable.land becomes the site specified in docs/. It has the Index, a Project page for run-run, vland, env and theme-toggle, and an og:image for each page. Astro 7 builds the site to static files, and the Worker serves them as assets, with no script.

 lab.variable.land  (Worker variablelab-www)
-  src/index.ts
-    /robots.txt                   → robots.txt
-    anything else                 → 302 github.com/withvariablelab
+  assets: dist/, html_handling drop-trailing-slash
+    /                             → the Index
+    /<project>                    → its Project page
+    /<project>/, /<project>.html  → 307 /<project>
+    /og.png, /og/<project>.png    → the og:images
+    /favicon.svg, /apple-touch-icon.png
+    /robots.txt                   → the same bytes, from public/
+    anything else                 → 404
src/
├── content/            # the Index and each Project, word for word from docs/content.md
├── content.config.ts   # their schemas, with the rules of docs/content-model.md
├── lib/                # derived fields, fixed labels, brand values, code colors, og:image layouts
├── layouts/  components/  styles/
└── pages/
    ├── index.astro  [project].astro
    └── og.png.ts  og/[project].png.ts  favicon.svg.ts  apple-touch-icon.png.ts
  • Brand values come from @variablelab/brand. Code is colored by Vesper's scopes, mapped to the brand's syntax roles.
  • CI uses brand's workflows at the same SHAs. On pull requests to main it runs rr jsc, astro check and astro build.
  • The implementation's decisions are in docs/design.md#decisions.

Evidence

Before, production today:

/            302 → https://github.com/withvariablelab
/robots.txt  200 text/plain; charset=utf-8

After, this branch's Preview at https://feat-site-variablelab-www.raquirozbazan.workers.dev:

/ /run-run /vland /env /theme-toggle  200 text/html
/env/ /env.html                       307 → /env
/nope                                 404
/og.png /og/<project>.png             200 image/png
/robots.txt                           200 text/plain

docs/design.md#verifying, run on the Preview: each page against its artboard. Each cell is the share of differing pixels over the full page. Every page is as tall as its artboard.

Page 1280 834 390
Index 0.01% 0.01% 0.02%
run-run 0.01% 0.02% 0.04%
vland 0.01% 0.01% 0.01%
env 0.02% 0.02% 0.04%
theme-toggle 0.02% 0.03% 0.06%
  • Code: each Snippet matches its artboard character by character, with the same color on each character.
  • Copy works from the keyboard and announces "Copied".
  • Moving between pages makes no new request for stylesheets or fonts. Each page paints 20–30 ms after its HTML arrives.

og:images, 1200×630, from the Preview:

Index og:image

run-run og:image

vland og:image

env og:image

theme-toggle og:image

Merge Danger

Door: two-way

Rolling back the Worker's deployment, or reverting this PR, brings the placeholder back. No response is cached for good. The 307s are temporary, and the only files cached as immutable are those under /_astro/, which are named by their content.

Blast Radius: production

  • / stops redirecting to GitHub and serves the Index.
  • /env keeps serving env's own landing until the two changes in docs/design.md#external-dependency land in the env repository. env's docs Worker claims lab.variable.land/env until then. Until then, the Index's and the Project pages' links to /env open env's landing.
  • /robots.txt keeps its bytes, but its content type loses ; charset=utf-8.
  • Each Project's docs under /<project>/* stay with that Project's own Worker.
  • Merging deploys: Workers Builds builds main and runs pnpm exec wrangler deploy, with the settings in README.md#deploying.

🤖 Generated with Claude Code

https://claude.ai/code/session_01QNKWAPoHSuv2GtmmNCpPiV

rqbazan and others added 18 commits October 16, 2026 21:33
Astro 7 builds the site statically; the Worker serves ./dist with no
script, and html_handling drop-trailing-slash redirects /<project>/ to
/<project>. robots.txt becomes a static file with the same bytes.

mise pins node and pnpm as in withvariablelab/brand and replaces
.node-version. rr runs Biome, which also lints and formats .astro files.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QNKWAPoHSuv2GtmmNCpPiV
Each Project is a YAML file and the Index another, with the values of
docs/content.md. The schema enforces docs/content-model.md's rules, and
src/lib/content.ts computes the derived fields. The Index page is a
temporary dump of that content until the real page lands.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QNKWAPoHSuv2GtmmNCpPiV
ci.yml, workflow-audit.yml and zizmor.yml come from withvariablelab/brand
with the same pinned actions. The Check job runs rr jsc, astro check and
astro build through pnpm, since mise-action only puts its shims on PATH.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QNKWAPoHSuv2GtmmNCpPiV
Only their path changes, as approved by the Keeper and drawn in the
canvas.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QNKWAPoHSuv2GtmmNCpPiV
One layout renders the head of docs/design.md and the header with the
Address and the Signature. site.css holds the tokens, Fontsource's woff2
files, the container, links, focus and the type scale. Shapes, the Copy
icons, the fixed UI labels and the code coloring are shared by the pages
and the og:images. favicon.svg and apple-touch-icon.png are built from
the Logo.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QNKWAPoHSuv2GtmmNCpPiV
The house's sentence and the Unpublished line, the Keeper's column, and
the Projects by Class, Apps first, as the canvas draws them at 1280, 834
and 390.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QNKWAPoHSuv2GtmmNCpPiV
The lead, the facts with the Install command box that copies it, the
Example with its Snippets colored by token, the Closing line and Also
indexed, as the canvas draws them for each Project at 1280, 834 and 390.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QNKWAPoHSuv2GtmmNCpPiV
/og.png and /og/<project>.png are drawn from the content with satori,
in IBM Plex from Fontsource's woff files, and rasterized with sharp.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QNKWAPoHSuv2GtmmNCpPiV
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QNKWAPoHSuv2GtmmNCpPiV
Every 14px small text is at line-height 1.5. Requires and Runs on wrap
pretty, so no fact ends on a single word. A Package too wide for its
column breaks after its scope, never at its hyphen. The og:image name
shrinks to the largest size that fits its column. In one column, focus
moves through the facts in the order they are drawn.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QNKWAPoHSuv2GtmmNCpPiV
The Index's title, every Project's title and og:site_name say Variable
Lab, Variable Land's open source arm. The Signature still sets Variable
Land beside the Logo.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QNKWAPoHSuv2GtmmNCpPiV
The Shiki theme is Vesper's own rules, each Vesper color swapped for the
syntax role that has it in tokens.json, as color.md now sets it: numbers,
booleans, types and support names take function; symbols take string;
the third accent is text. Three scopes keep the canvas's role: the * of
import * as, an HTML attribute's =, and JSX braces.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QNKWAPoHSuv2GtmmNCpPiV
The canvas now colors the * of import * as and an HTML attribute's = as
text, and JSX braces as keyword, as Vesper does, so the theme needs no
rule of its own.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QNKWAPoHSuv2GtmmNCpPiV
Its color.md colors code by Vesper's scopes; no token value changes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QNKWAPoHSuv2GtmmNCpPiV
Runs on keeps its last two Runtimes on one line, so env's list never
ends on a single word, as it did at 351px. On the og:image, the Sign and
the name sit at whole-pixel insets, so theme-toggle's 71px name lands on
the same row as in the canvas.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QNKWAPoHSuv2GtmmNCpPiV
README gets the Build command and NODE_VERSION in Deploying, how preview
triggers and Preview URLs work, and the commands run through mise. It
also says why www must never redirect /<project> to /<project>/.
AGENTS.md drops the placeholder Worker and says where content, labels,
brand values and code colors live. design.md's Open decisions become
Decisions, and its Head lists <title> and the description.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QNKWAPoHSuv2GtmmNCpPiV
The Signature's name moves to labels.ts with the other fixed words.
Comments drop canvas measurements and history and state the rule.
add-a-project.md says where a Project's content goes in this repo.
design.md records how the Install box and Published as fit at narrow
widths. .gitignore drops the Worker's .dev.vars.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QNKWAPoHSuv2GtmmNCpPiV
Every asset was served with max-age=0, must-revalidate, so each page
waited on a round trip for its two stylesheets and revalidated its
fonts before painting. Stylesheets are now inlined, the three fonts are
preloaded, and public/_headers caches the content-named files under
/_astro/ for a year as immutable.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QNKWAPoHSuv2GtmmNCpPiV
@changeset-bot

changeset-bot Bot commented Oct 17, 2026

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: 13173f8

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@cloudflare-workers-and-pages

Copy link
Copy Markdown

🚀 Deploying Preview to Cloudflare 🚀

Preview URL: https://feat-site-variablelab-www.raquirozbazan.workers.dev (commit 13173f8)

This URL reflects your latest Preview deployment

Preview Deployments by commit

Status Deployment URL Commit Updated (UTC) See this deployment's details
  • Build: Success ✅
  • Deployment: Success ✅

View logs ↗
https://a1cbb726-variablelab-www.raquirozbazan.workers.dev 13173f8 2026-10-17T14:55:53.738Z Visit the dashboard ↗

@rqbazan
rqbazan merged commit a2c1e20 into main Oct 17, 2026
3 checks passed
@rqbazan
rqbazan deleted the feat/site branch October 17, 2026 19:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant