From b2a26d8e45f2c77b013e6fb1dd41320a6096aca0 Mon Sep 17 00:00:00 2001
From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com>
Date: Wed, 17 Jun 2026 19:13:57 +0200
Subject: [PATCH 1/3] Redo the landing page: make the voice more mine, if I can
say so.
---
astro.config.mjs | 10 +
package-lock.json | 47 +-
package.json | 4 +-
src/home/index.mdx | 41 ++
src/layouts/Base.astro | 9 +-
src/pages/index.astro | 1464 ++++------------------------------------
src/styles/global.css | 32 -
src/styles/home.css | 266 ++++++++
8 files changed, 483 insertions(+), 1390 deletions(-)
create mode 100644 src/home/index.mdx
create mode 100644 src/styles/home.css
diff --git a/astro.config.mjs b/astro.config.mjs
index 43537ff..c77788b 100644
--- a/astro.config.mjs
+++ b/astro.config.mjs
@@ -1,10 +1,17 @@
import { defineConfig } from "astro/config";
import starlight from "@astrojs/starlight";
+import mdx from "@astrojs/mdx";
export default defineConfig({
site: process.env.SITE_URL ?? "https://wezel.build",
base: process.env.SITE_BASE ?? "/",
+ // Code blocks in the hand-authored landing body (src/home/index.mdx).
+ // Starlight manages its own theming for docs separately.
+ markdown: {
+ shikiConfig: { theme: "vitesse-dark", wrap: false },
+ },
+
integrations: [
starlight({
title: "Wezel Docs",
@@ -52,5 +59,8 @@ export default defineConfig({
},
],
}),
+ // mdx() must come after starlight() — Starlight registers
+ // expressive-code, which has to be set up before MDX.
+ mdx(),
],
});
diff --git a/package-lock.json b/package-lock.json
index 68dafbe..21a3be5 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -8,11 +8,13 @@
"name": "wezel-www",
"version": "0.0.1",
"dependencies": {
+ "@astrojs/mdx": "^4.3.14",
"@astrojs/starlight": "^0.37.6",
"@fontsource/ibm-plex-sans": "^5.2.8",
"@fontsource/iosevka": "^5.2.5",
"@fontsource/iosevka-aile": "^5.2.5",
- "astro": "^5.10.0"
+ "astro": "^5.10.0",
+ "ci-info": "^4.4.0"
}
},
"node_modules/@astrojs/compiler": {
@@ -57,12 +59,12 @@
}
},
"node_modules/@astrojs/mdx": {
- "version": "4.3.13",
- "resolved": "https://registry.npmjs.org/@astrojs/mdx/-/mdx-4.3.13.tgz",
- "integrity": "sha512-IHDHVKz0JfKBy3//52JSiyWv089b7GVSChIXLrlUOoTLWowG3wr2/8hkaEgEyd/vysvNQvGk+QhysXpJW5ve6Q==",
+ "version": "4.3.14",
+ "resolved": "https://registry.npmjs.org/@astrojs/mdx/-/mdx-4.3.14.tgz",
+ "integrity": "sha512-FBrqJQORVm+rkRa2TS5CjU9PBA6hkhrwLVBSS9A77gN2+iehvjq1w6yya/d0YKC7osiVorKkr3Qd9wNbl0ZkGA==",
"license": "MIT",
"dependencies": {
- "@astrojs/markdown-remark": "6.3.10",
+ "@astrojs/markdown-remark": "6.3.11",
"@mdx-js/mdx": "^3.1.1",
"acorn": "^8.15.0",
"es-module-lexer": "^1.7.0",
@@ -83,6 +85,41 @@
"astro": "^5.0.0"
}
},
+ "node_modules/@astrojs/mdx/node_modules/@astrojs/internal-helpers": {
+ "version": "0.7.6",
+ "resolved": "https://registry.npmjs.org/@astrojs/internal-helpers/-/internal-helpers-0.7.6.tgz",
+ "integrity": "sha512-GOle7smBWKfMSP8osUIGOlB5kaHdQLV3foCsf+5Q9Wsuu+C6Fs3Ez/ttXmhjZ1HkSgsogcM1RXSjjOVieHq16Q==",
+ "license": "MIT"
+ },
+ "node_modules/@astrojs/mdx/node_modules/@astrojs/markdown-remark": {
+ "version": "6.3.11",
+ "resolved": "https://registry.npmjs.org/@astrojs/markdown-remark/-/markdown-remark-6.3.11.tgz",
+ "integrity": "sha512-hcaxX/5aC6lQgHeGh1i+aauvSwIT6cfyFjKWvExYSxUhZZBBdvCliOtu06gbQyhbe0pGJNoNmqNlQZ5zYUuIyQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@astrojs/internal-helpers": "0.7.6",
+ "@astrojs/prism": "3.3.0",
+ "github-slugger": "^2.0.0",
+ "hast-util-from-html": "^2.0.3",
+ "hast-util-to-text": "^4.0.2",
+ "import-meta-resolve": "^4.2.0",
+ "js-yaml": "^4.1.1",
+ "mdast-util-definitions": "^6.0.0",
+ "rehype-raw": "^7.0.0",
+ "rehype-stringify": "^10.0.1",
+ "remark-gfm": "^4.0.1",
+ "remark-parse": "^11.0.0",
+ "remark-rehype": "^11.1.2",
+ "remark-smartypants": "^3.0.2",
+ "shiki": "^3.21.0",
+ "smol-toml": "^1.6.0",
+ "unified": "^11.0.5",
+ "unist-util-remove-position": "^5.0.0",
+ "unist-util-visit": "^5.0.0",
+ "unist-util-visit-parents": "^6.0.2",
+ "vfile": "^6.0.3"
+ }
+ },
"node_modules/@astrojs/prism": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/@astrojs/prism/-/prism-3.3.0.tgz",
diff --git a/package.json b/package.json
index 41965d8..bdfc06b 100644
--- a/package.json
+++ b/package.json
@@ -8,10 +8,12 @@
"preview": "astro preview"
},
"dependencies": {
+ "@astrojs/mdx": "^4.3.14",
"@astrojs/starlight": "^0.37.6",
"@fontsource/ibm-plex-sans": "^5.2.8",
"@fontsource/iosevka": "^5.2.5",
"@fontsource/iosevka-aile": "^5.2.5",
- "astro": "^5.10.0"
+ "astro": "^5.10.0",
+ "ci-info": "^4.4.0"
}
}
diff --git a/src/home/index.mdx b/src/home/index.mdx
new file mode 100644
index 0000000..016c1c7
--- /dev/null
+++ b/src/home/index.mdx
@@ -0,0 +1,41 @@
+## Why?
+
+Builds get slower one commit at a time, and nobody notices until the whole
+team is waiting. Wezel watches the build scenarios you actually run and tells
+you the moment one of them regresses — while the change is still fresh enough
+to fix.
+
+## What
+
+An `experiment.toml`, committed next to your code, declares what to measure.
+The `cargo` tool builds your target and emits per-crate timing outcomes, and a
+single outcome can feed several summaries.
+
+```toml
+description = "Tracks compile time of the wezel workspace"
+
+[step.cargo.build]
+build_target = "workspace"
+
+summary.workspace-build-time = { outcome = "duration.total" }
+summary.slowest-crate-time = { outcome = "duration.max" }
+```
+
+On every commit, the CLI walks the steps, captures the outcomes, and compares
+each summary against the trailing baseline. When a summary drifts past
+tolerance, Wezel reports the offending commit, the delta, and the affected
+scope.
+
+```console
+$ wezel experiment run workspace-build
+→ step.cargo.build ........... 59.7s
+→ summary.workspace-build-time 59.7s (baseline 47.3s · +12.4s · regression)
+→ summary.slowest-crate-time 8.4s (baseline 8.1s · +0.3s)
+done in 59.9s
+```
+
+## Stay tuned
+
+Wezel is open source and under active development. Follow along on
+[GitHub](https://github.com/wezel-build/wezel), or read the
+[quickstart](/docs/quickstart) to wire it into your own builds.
diff --git a/src/layouts/Base.astro b/src/layouts/Base.astro
index 5c9a0d2..c2a632a 100644
--- a/src/layouts/Base.astro
+++ b/src/layouts/Base.astro
@@ -31,8 +31,11 @@ const themes = ["dark", "light"] as const;
@@ -60,7 +63,9 @@ const themes = ["dark", "light"] as const;
(function() {
var order = ['dark', 'light'];
var btn = document.getElementById('theme-toggle');
- var current = localStorage.getItem('theme') || 'dark';
+ // Reflect whatever was actually applied (which may be the
+ // landing's light default rather than a stored value).
+ var current = document.documentElement.getAttribute('data-theme') || 'dark';
btn.textContent = current;
btn.addEventListener('click', function() {
var i = order.indexOf(current);
diff --git a/src/pages/index.astro b/src/pages/index.astro
index 3b096d3..2a9926f 100644
--- a/src/pages/index.astro
+++ b/src/pages/index.astro
@@ -1,1358 +1,122 @@
---
-import Base from '../layouts/Base.astro';
-import { Code } from 'astro:components';
-
-const installUrl = new URL('install.sh', Astro.site ?? 'https://wezel.build/').toString();
+import Base from "../layouts/Base.astro";
+import "../styles/home.css";
+import { Content as Body } from "../home/index.mdx";
+
+const installUrl = new URL(
+ "install.sh",
+ Astro.site ?? "https://wezel.build/",
+).toString();
const installCmd = `curl -fsSL ${installUrl} | sh`;
-
-const experimentToml = `description = "Tracks compile time of the wezel workspace"
-
-# Steps are keyed [step..].
-# The cargo tool builds your target and emits per-crate timing outcomes.
-[step.cargo.build]
-build_target = "workspace"
-
-# A single outcome can feed multiple summaries — Wezel tracks each independently.
-summary.workspace-build-time = { outcome = "duration.total" }
-summary.slowest-crate-time = { outcome = "duration.max" }
-`;
-
-// Chart data: Wezel samples build durations across recent commits, then bisects
-// when a sample drifts. Most commits are unmeasured (small ticks); a handful are
-// sampled bars; two get measured via bisection to pinpoint the offender.
-const N = 32;
-const BASELINE = 47.3;
-const W = 480;
-const H = 210;
-const PAD_X = 4;
-const slot = (W - PAD_X * 2) / N;
-const barW = slot * 0.62;
-const baseY = 165;
-const scale = 2.1;
-
-// Samples land at irregular intervals (the schedule isn't uniform in real use).
-const sampleIdxs = [4, 11, 13, 17]; // gaps of 7, 2, 4 commits
-const detectIdx = N - 1; // detect is the latest commit (rightmost slot)
-// Binary search on [18..30] with 31 known bad and 17 known good. The
-// terminating condition is a measured-bad commit adjacent to a measured-good
-// commit — so the offender itself has to be measured, not inferred.
-// 1. mid of [18,30] = 24 → measure (BAD). Narrow LEFT to [18,23].
-// 2. mid of [18,23] = 20 → measure (good). Narrow RIGHT to [21,23].
-// 3. mid of [21,23] = 22 → measure (good). Narrow RIGHT to [23,23].
-// 4. last candidate: 23 → measure (BAD). 22-good and 23-bad adjacent: 23.
-const bisectStep1Idx = 24; // bad
-const bisectStep2Idx = 20; // good — jump back-left after step 1 was bad
-const bisectStep3Idx = 22; // good — narrow further right
-const bisectStep4Idx = 23; // bad — confirms 23 is the offender (adjacent to 22-good)
-const regressionIdx = bisectStep4Idx;
-
-// Timing: the cursor sweeps left-to-right across the chart at SWEEP_MOVING_MS
-// of pure motion, but PAUSES for PAUSE_MS at each sample commit (so the viewer
-// sees the cursor visibly stop on every measurement). Total sweep duration
-// extends by the cumulative pause time.
-// Bisection bars appear AFTER the sweep finishes, with the cursor jumping back
-// to track each binary-search step.
-const SWEEP_MOVING_MS = 4000;
-const STEP_MS = SWEEP_MOVING_MS / N;
-const PAUSE_MS = 150;
-const TOTAL_PAUSE_MS = sampleIdxs.length * PAUSE_MS;
-const SWEEP_END_MS = SWEEP_MOVING_MS + TOTAL_PAUSE_MS; // 4600ms
-
-// pausesBefore(i): total ms of cursor-pause accumulated by the time the cursor
-// reaches commit i's center (count of sample indices strictly less than i).
-function pausesBefore(i) {
- let n = 0;
- for (const s of sampleIdxs) if (s < i) n++;
- return n * PAUSE_MS;
-}
-
-const BISECT_SPAN_DELAY = SWEEP_END_MS + 200; // 4800 — bracket starts drawing right-to-left here
-const BISECT_STEP_1_DELAY = SWEEP_END_MS + 1300; // 5900 — wait for bracket+label to fully settle (~5700ms), small buffer
-const BISECT_STEP_2_DELAY = SWEEP_END_MS + 1725; // 6325
-const BISECT_STEP_3_DELAY = SWEEP_END_MS + 2150; // 6750
-const BISECT_STEP_4_DELAY = SWEEP_END_MS + 2575; // 7175
-const MARKER_LINE_DELAY = SWEEP_END_MS + 2950; // 7550
-const MARKER_LABEL_DELAY = SWEEP_END_MS + 3100; // 7700
-
-const commits = Array.from({ length: N }, (_, i) => {
- const tBase = Math.round(i * STEP_MS + pausesBefore(i));
- if (i === bisectStep1Idx)
- return { i, type: 'bisect-bad', value: 59.5, delay: BISECT_STEP_1_DELAY };
- if (i === bisectStep2Idx)
- return { i, type: 'bisect-good', value: 47.1, delay: BISECT_STEP_2_DELAY };
- if (i === bisectStep3Idx)
- return { i, type: 'bisect-good', value: 47.0, delay: BISECT_STEP_3_DELAY };
- if (i === bisectStep4Idx)
- return { i, type: 'bisect-bad', value: 59.6, delay: BISECT_STEP_4_DELAY };
- if (i === detectIdx)
- return { i, type: 'detect', value: 59.3, delay: tBase + 80 };
- if (sampleIdxs.includes(i))
- return {
- i,
- type: 'sample',
- value: BASELINE + Math.sin(i * 1.7) * 0.5,
- delay: tBase + 80,
- };
- return { i, type: 'unmeasured', value: null, delay: tBase };
-});
---
-
-
-
-
-
-
-
-
- Build-time observability · open source
-
-
- Your build,
- always at its best.
-
-
- Wezel benchmarks the build scenarios your team runs most, on every commit.
- When something gets slower, the offending change, the delta, and the affected scope
- surface immediately.
-
-
-
-
+
-
-
diff --git a/src/styles/global.css b/src/styles/global.css
index 83e4b35..20eb888 100644
--- a/src/styles/global.css
+++ b/src/styles/global.css
@@ -167,18 +167,11 @@ samp {
background: var(--accent);
color: var(--bg);
border-color: var(--accent);
- box-shadow:
- 0 0 20px var(--accent-glow),
- 0 1px 3px rgba(0, 0, 0, 0.3);
}
.btn-primary:hover {
background: #c09878;
border-color: #c09878;
- box-shadow:
- 0 0 32px var(--accent-glow-strong),
- 0 2px 8px rgba(0, 0, 0, 0.4);
- transform: translateY(-1px);
}
.btn-secondary {
@@ -277,40 +270,15 @@ samp {
}
/* ===== Subtle noise texture on body ===== */
-[data-theme="light"] .btn-primary {
- box-shadow:
- 0 0 16px var(--accent-glow),
- 0 1px 3px rgba(0, 0, 0, 0.1);
-}
-
[data-theme="light"] .btn-primary:hover {
background: #7a5028;
border-color: #7a5028;
- box-shadow:
- 0 0 24px var(--accent-glow-strong),
- 0 2px 6px rgba(0, 0, 0, 0.12);
}
[data-theme="light"] .card:hover {
box-shadow: 0 4px 24px rgba(0, 0, 0, 0.06);
}
-body::before {
- content: "";
- position: fixed;
- inset: 0;
- pointer-events: none;
- z-index: 9999;
- opacity: 0.015;
- background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 256 256' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
- background-repeat: repeat;
- background-size: 256px 256px;
-}
-
-[data-theme="light"] body::before {
- opacity: 0;
-}
-
/* ===== Responsive ===== */
@media (max-width: 768px) {
.container {
diff --git a/src/styles/home.css b/src/styles/home.css
new file mode 100644
index 0000000..4415ef0
--- /dev/null
+++ b/src/styles/home.css
@@ -0,0 +1,266 @@
+/* Landing page — light warm-paper, prose-forward, README-like.
+ Centered wordmark hero; body reads as a styled document with accent-bar
+ section headings. Styling only; words live in index.astro (hero) and
+ src/home/index.mdx (body). */
+
+/* ── Hero (centered wordmark) ─────────────────────────── */
+.hero {
+ text-align: center;
+ padding: 5.5rem 1.5rem 3.5rem;
+ border-bottom: 1px solid var(--border);
+}
+.hero-inner {
+ max-width: 44rem;
+ margin: 0 auto;
+}
+.hero-mark {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ width: 48px;
+ height: 48px;
+ margin: 0 auto 1.5rem;
+ border-radius: 12px;
+ background: var(--accent);
+ color: var(--bg);
+ font-family: var(--font-mono);
+ font-weight: 700;
+ font-size: 28px;
+ line-height: 1;
+}
+.hero-word {
+ font-family: var(--font-display);
+ font-weight: 700;
+ font-size: clamp(2.6rem, 6vw, 3.6rem);
+ letter-spacing: -0.04em;
+ line-height: 1;
+ color: var(--text);
+ margin: 0;
+}
+.hero-tagline {
+ font-family: var(--font-mono);
+ font-size: 0.95rem;
+ letter-spacing: 0.01em;
+ color: var(--accent);
+ margin: 0.6rem 0 0;
+}
+.hero-sub {
+ font-size: 1.02rem;
+ line-height: 1.65;
+ color: var(--text-mid);
+ max-width: 48ch;
+ margin: 1.5rem auto 0;
+}
+.hero-banner {
+ display: inline-block;
+ margin: 1.75rem auto 0;
+ padding: 0.4rem 0.9rem;
+ border: 1px solid var(--border-light);
+ border-radius: 999px;
+ font-family: var(--font-mono);
+ font-size: 0.7rem;
+ letter-spacing: 0.1em;
+ text-transform: uppercase;
+ color: var(--text-dim);
+}
+.hero-ctas {
+ display: flex;
+ gap: 0.75rem;
+ justify-content: center;
+ flex-wrap: wrap;
+ margin-top: 2rem;
+}
+
+/* ── Body (hand-authored MDX prose) ───────────────────── */
+.body {
+ border-bottom: 1px solid var(--border);
+}
+.prose {
+ max-width: 44rem;
+ margin: 0 auto;
+ padding: 4rem 1.5rem 4.5rem;
+}
+.prose h2 {
+ position: relative;
+ font-family: var(--font-display);
+ font-weight: 700;
+ font-size: 1.5rem;
+ letter-spacing: -0.03em;
+ color: var(--text);
+ padding-left: 0.85rem;
+ margin: 3rem 0 1rem;
+}
+.prose h2:first-child {
+ margin-top: 0;
+}
+/* the little accent bar that gives the dodeca/vixen heading feel */
+.prose h2::before {
+ content: "";
+ position: absolute;
+ left: 0;
+ top: 0.2em;
+ bottom: 0.15em;
+ width: 4px;
+ border-radius: 2px;
+ background: var(--accent);
+}
+.prose h3 {
+ font-family: var(--font-display);
+ font-weight: 600;
+ font-size: 1.15rem;
+ letter-spacing: -0.02em;
+ color: var(--text);
+ margin: 2rem 0 0.75rem;
+}
+.prose p {
+ font-size: 1.02rem;
+ line-height: 1.78;
+ color: var(--text-mid);
+ margin: 0 0 1.15rem;
+}
+.prose ul,
+.prose ol {
+ margin: 0 0 1.15rem 1.25rem;
+ list-style: revert;
+ color: var(--text-mid);
+ line-height: 1.78;
+}
+.prose li {
+ margin: 0.3rem 0;
+}
+.prose a {
+ color: var(--accent);
+ border-bottom: 1px solid color-mix(in srgb, var(--accent) 35%, transparent);
+ transition: border-color 0.15s;
+}
+.prose a:hover {
+ border-bottom-color: var(--accent);
+}
+/* inline code — subtle tint, the way vixen styles it */
+.prose :not(pre) > code {
+ font-family: var(--font-mono);
+ font-size: 0.88em;
+ color: var(--accent);
+ background: var(--accent-glow);
+ padding: 0.1em 0.36em;
+ border-radius: 4px;
+}
+
+/* ── Code blocks (expressive-code), flattened ─────────── */
+/* Strip the window chrome so blocks read as flat documents, not a
+ terminal app. Token colors still come from expressive-code's themes,
+ which track the light/dark toggle. */
+.prose .expressive-code {
+ margin: 1.5rem 0;
+}
+.prose .expressive-code .frame {
+ box-shadow: none !important;
+}
+.prose .expressive-code .frame .header {
+ display: none !important;
+}
+.prose .expressive-code pre {
+ border: 1px solid var(--border);
+ border-radius: var(--radius);
+ padding: 1rem 1.15rem;
+}
+
+/* ── Install snippet (flat, centered in closing) ──────── */
+.install-snippet {
+ width: 100%;
+ max-width: 540px;
+ margin: 0 auto;
+ text-align: left;
+ background: var(--surface);
+ border: 1px solid var(--border);
+ border-radius: var(--radius);
+ overflow: hidden;
+}
+.install-snippet figcaption {
+ display: flex;
+ align-items: center;
+ gap: 0.5rem;
+ padding: 0.5rem 0.9rem;
+ background: var(--bg-deep);
+ border-bottom: 1px solid var(--border);
+ font-family: var(--font-mono);
+ font-size: 0.66rem;
+ letter-spacing: 0.12em;
+ text-transform: uppercase;
+ color: var(--text-dim);
+}
+.install-shell {
+ color: var(--text-dim);
+}
+.install-copy {
+ margin-left: auto;
+ background: transparent;
+ border: 1px solid var(--border);
+ border-radius: 4px;
+ padding: 0.15rem 0.55rem;
+ font-family: var(--font-mono);
+ font-size: 0.64rem;
+ letter-spacing: 0.12em;
+ text-transform: uppercase;
+ color: var(--text-mid);
+ cursor: pointer;
+ transition: border-color 0.15s, color 0.15s, background 0.15s;
+}
+.install-copy:hover {
+ color: var(--text);
+ border-color: var(--text-dim);
+}
+.install-copy-done {
+ color: var(--green) !important;
+ border-color: var(--green) !important;
+ background: var(--green-dim) !important;
+}
+.install-snippet pre {
+ margin: 0;
+ padding: 0.95rem 1.1rem;
+ font-family: var(--font-mono);
+ font-size: 0.84rem;
+ line-height: 1.6;
+ color: var(--text);
+ background: transparent;
+ overflow-x: auto;
+ white-space: pre;
+}
+.install-snippet .t-prompt { color: var(--green); font-weight: 600; margin-right: 0.5em; }
+.install-snippet .t-cmd { color: var(--accent); font-weight: 500; }
+.install-snippet .t-flag { color: var(--text-mid); }
+.install-snippet .t-url { color: var(--cyan); }
+.install-snippet .t-pipe { color: var(--text-mid); margin: 0 0.15em; }
+
+/* ── Closing (quiet, centered) ────────────────────────── */
+.closing {
+ text-align: center;
+ padding: 4rem 1.5rem 5rem;
+}
+.closing-inner {
+ max-width: 44rem;
+ margin: 0 auto;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ gap: 1.5rem;
+}
+.closing-lead {
+ font-size: 1.05rem;
+ color: var(--text-mid);
+ margin: 0;
+}
+.closing-links {
+ font-family: var(--font-mono);
+ font-size: 0.85rem;
+ color: var(--text-dim);
+}
+.closing-links a { color: var(--accent); }
+.closing-links a:hover { color: var(--text); }
+
+/* ── Responsive ───────────────────────────────────────── */
+@media (max-width: 700px) {
+ .hero { padding: 3.5rem 1.25rem 2.5rem; }
+ .prose { padding: 3rem 1.25rem 3.5rem; }
+ .closing { padding: 3rem 1.25rem 4rem; }
+}
From 2e1d5dd9d607abf8881ad7e0888aece7edc8d9c1 Mon Sep 17 00:00:00 2001
From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com>
Date: Wed, 17 Jun 2026 21:48:37 +0200
Subject: [PATCH 2/3] Initial massages
---
src/home/index.mdx | 57 +++++++++++++++++++++++++++++++++-------------
1 file changed, 41 insertions(+), 16 deletions(-)
diff --git a/src/home/index.mdx b/src/home/index.mdx
index 016c1c7..c6c26a0 100644
--- a/src/home/index.mdx
+++ b/src/home/index.mdx
@@ -1,30 +1,55 @@
## Why?
-Builds get slower one commit at a time, and nobody notices until the whole
-team is waiting. Wezel watches the build scenarios you actually run and tells
-you the moment one of them regresses — while the change is still fresh enough
-to fix.
+I feel like build times are an afterthought. Yes, people spend countless hours complaining about them. Yes, the pain is real. Yet the trend is for them to get worse, not better. There's never enough time in a day to get them into shape.
+
+We do not talk about our issues enough, because nobody wishes to *care* about their build. It's a boring topic. Rightly so. You want to spend your sweet time on tackling problems you're paid to solve.
+
+### Sufficiently swift compiler
+It's not like we can expect a sufficiently swift compiler to show up and wave all of your issues away either. We cannot expect toolchain maintainers to:
+1. know what is painful about your usage of the toolchain.
+2. have access to your codebase in order to poke, prod and profile a compiler.
+
+Even if such compiler existed, chances are that [Jevons paradox](https://en.wikipedia.org/wiki/Jevons_paradox) would do you dirty.
+
+### Optimizing builds requires obscure knowledge
+
+Material on optimizing builds is few and far between, stowed away on internet forums or blog post entries. In a way, you have to have quite intricate understanding of the compiler internals in order to really squeeze your build.
+Knowing how big of an impact that using a given dependency will have on your builds is nigh impossible. So is structuring code around monomorphizations or dynamic dispatch. Yet, down the line, these decisions can have collateral effect on your build performance.
+
+### Speed is a cultural challenge
+It's quite tricky to keep code written a particular way. In my experience, code that's quicker to compile is "uglier", as you have to limit how much a compiler has to do. Sure, you can stick a comment on it, but there's no way to be *certain* that code you've made quick to compile will stay that way - especially in the era where the human might not necessarily be in the loop.
+
+### Putting it all together
+We're left in a situation where optimizing builds is a skill on its own, whose fruits are hard to hold onto. Nobody will do that for you and while you may be tempted to just skip this relatively boring chore, you'll feel the pain of that negligence yourself. That's why I've built Wezel - I want people to know when (and eventually - why) their build regressed
## What
-An `experiment.toml`, committed next to your code, declares what to measure.
-The `cargo` tool builds your target and emits per-crate timing outcomes, and a
+Wezel lets you define build scenarios you want to track across the lifetime of your project.
+It all starts with an `experiment.toml`, committed next to your code, that declares what to measure.
+Take the following example The `cargo` tool builds your target and emits timing outcomes, and a
single outcome can feed several summaries.
```toml
description = "Tracks compile time of the wezel workspace"
-[step.cargo.build]
-build_target = "workspace"
-
-summary.workspace-build-time = { outcome = "duration.total" }
-summary.slowest-crate-time = { outcome = "duration.max" }
+[step.cargo.release-build]
+command = "build"
+build_target = ["burrow"]
+profile = "release"
+# Summaries are outputs of a step of an experiment.
+# They're always scalar (to drive the bisection).
+# Summaries use outcomes to get their values.
+# Outcomes are results of your experiments - they are not necessarily scalar!
+# (think of e.g. compiler profiling files or cargo --timings output)
+#
+# In this case, `cargo` tool emits `time_ms` for a total time for a build.
+# In order to produce a summary,
+# we'll run 5 builds and take their mean build time as a value of `build-time` summary.
+summary.build-time = { outcome = "time_ms", aggregation = "mean", samples = 5 }
```
-On every commit, the CLI walks the steps, captures the outcomes, and compares
-each summary against the trailing baseline. When a summary drifts past
-tolerance, Wezel reports the offending commit, the delta, and the affected
-scope.
+Most commits do not meaningfully regress your builds though. That's why we resort to sparse sampling: instead of running these experiments on every single commit you make, we run these measurements once a day or so.
+Only when there's a meaningful regression between the last measurement and a current one do we resort to running experiments on all unmeasured commits - until all sources of regressions are found.
```console
$ wezel experiment run workspace-build
@@ -34,7 +59,7 @@ $ wezel experiment run workspace-build
done in 59.9s
```
-## Stay tuned
+## Where
Wezel is open source and under active development. Follow along on
[GitHub](https://github.com/wezel-build/wezel), or read the
From b471ab3ecc2cb84995f7a277a8b7d581b0494445 Mon Sep 17 00:00:00 2001
From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com>
Date: Thu, 18 Jun 2026 10:19:02 +0200
Subject: [PATCH 3/3] Land simplified page, v0
---
astro.config.mjs | 14 +-
public/favicon.svg | 4 +
src/content.config.ts | 17 +--
src/content/docs/docs/index.md | 5 -
src/home/index.mdx | 17 +--
src/layouts/Base.astro | 55 +------
src/lib/authors.ts | 31 ----
src/pages/blog/[id].astro | 259 ---------------------------------
src/pages/blog/index.astro | 201 -------------------------
src/styles/global.css | 45 +-----
src/styles/starlight.css | 83 +++++++++++
11 files changed, 117 insertions(+), 614 deletions(-)
create mode 100644 public/favicon.svg
delete mode 100644 src/content/docs/docs/index.md
delete mode 100644 src/lib/authors.ts
delete mode 100644 src/pages/blog/[id].astro
delete mode 100644 src/pages/blog/index.astro
create mode 100644 src/styles/starlight.css
diff --git a/astro.config.mjs b/astro.config.mjs
index c77788b..fce42ac 100644
--- a/astro.config.mjs
+++ b/astro.config.mjs
@@ -6,6 +6,11 @@ export default defineConfig({
site: process.env.SITE_URL ?? "https://wezel.build",
base: process.env.SITE_BASE ?? "/",
+ // /docs has no landing page of its own — send it to the introduction.
+ redirects: {
+ "/docs": "/docs/introduction",
+ },
+
// Code blocks in the hand-authored landing body (src/home/index.mdx).
// Starlight manages its own theming for docs separately.
markdown: {
@@ -47,8 +52,15 @@ export default defineConfig({
autogenerate: { directory: "docs/developing" },
},
],
- customCss: [],
+ customCss: ["./src/styles/starlight.css"],
head: [
+ // Light-only: pin the theme so expressive-code and everything else
+ // render light, regardless of OS preference or any stored value.
+ {
+ tag: 'script',
+ content:
+ "try{localStorage.setItem('starlight-theme','light')}catch(e){}document.documentElement.dataset.theme='light';",
+ },
{
tag: 'script',
attrs: {
diff --git a/public/favicon.svg b/public/favicon.svg
new file mode 100644
index 0000000..cdcd74e
--- /dev/null
+++ b/public/favicon.svg
@@ -0,0 +1,4 @@
+
diff --git a/src/content.config.ts b/src/content.config.ts
index e32fee2..43fb7a6 100644
--- a/src/content.config.ts
+++ b/src/content.config.ts
@@ -1,23 +1,10 @@
-import { defineCollection, z } from "astro:content";
+import { defineCollection } from "astro:content";
import { docsLoader } from "@astrojs/starlight/loaders";
import { docsSchema } from "@astrojs/starlight/schema";
-import { glob } from "astro/loaders";
export const collections = {
docs: defineCollection({
loader: docsLoader(),
schema: docsSchema(),
}),
- blog: defineCollection({
- loader: glob({ pattern: "**/*.md", base: "./src/content/blog" }),
- schema: z.object({
- title: z.string(),
- description: z.string(),
- date: z.coerce.date(),
- draft: z.boolean().default(false),
- author: z
- .union([z.string(), z.array(z.string()).min(1)])
- .transform((v) => (Array.isArray(v) ? v : [v])),
- }),
- }),
-};
\ No newline at end of file
+};
diff --git a/src/content/docs/docs/index.md b/src/content/docs/docs/index.md
deleted file mode 100644
index 696d534..0000000
--- a/src/content/docs/docs/index.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Hello
----
-
-Hello world.
\ No newline at end of file
diff --git a/src/home/index.mdx b/src/home/index.mdx
index c6c26a0..65990dc 100644
--- a/src/home/index.mdx
+++ b/src/home/index.mdx
@@ -32,6 +32,7 @@ single outcome can feed several summaries.
```toml
description = "Tracks compile time of the wezel workspace"
+# A single experiment consists of multiple steps, executed in order
[step.cargo.release-build]
command = "build"
build_target = ["burrow"]
@@ -51,16 +52,14 @@ summary.build-time = { outcome = "time_ms", aggregation = "mean", samples = 5 }
Most commits do not meaningfully regress your builds though. That's why we resort to sparse sampling: instead of running these experiments on every single commit you make, we run these measurements once a day or so.
Only when there's a meaningful regression between the last measurement and a current one do we resort to running experiments on all unmeasured commits - until all sources of regressions are found.
-```console
-$ wezel experiment run workspace-build
-→ step.cargo.build ........... 59.7s
-→ summary.workspace-build-time 59.7s (baseline 47.3s · +12.4s · regression)
-→ summary.slowest-crate-time 8.4s (baseline 8.1s · +0.3s)
-done in 59.9s
-```
+The results of experiment runs are surfaced in a dashboard over at [app.wezel.build](app.wezel.build).
+
+### Not just Rust
+
+The whole thing is extensible - tool implementations live in separate binaries. Nothing prevents you from adding support for your own toolchain of choice - as long as you follow the protocol.
## Where
Wezel is open source and under active development. Follow along on
-[GitHub](https://github.com/wezel-build/wezel), or read the
-[quickstart](/docs/quickstart) to wire it into your own builds.
+[GitHub](https://github.com/wezel-build/wezel), read the
+[quickstart](/docs/quickstart) to wire it into your own builds. There's also [Discord server](https://discord.gg/NE6HAz7H) you can join to chat and follow along.
diff --git a/src/layouts/Base.astro b/src/layouts/Base.astro
index c2a632a..c7eab0a 100644
--- a/src/layouts/Base.astro
+++ b/src/layouts/Base.astro
@@ -9,36 +9,24 @@ interface Props {
const { title = "Wezel — Your build, always at its best." } = Astro.props;
const navLinks = [
- { label: "Blog", href: "/blog" },
{ label: "Docs", href: "/docs" },
{ label: "Status", href: "https://status.wezel.build" },
];
-const themes = ["dark", "light"] as const;
---
-
+
-
+
{title}
-