diff --git a/site/.gitignore b/site/.gitignore
index c626920..adaa241 100644
--- a/site/.gitignore
+++ b/site/.gitignore
@@ -2,3 +2,4 @@ node_modules
dist
.vercel
cache
+src/pages.gen.ts
diff --git a/site/components/FamilyNav.tsx b/site/components/FamilyNav.tsx
new file mode 100644
index 0000000..e09b2b2
--- /dev/null
+++ b/site/components/FamilyNav.tsx
@@ -0,0 +1,47 @@
+/**
+ * Cross-repo navigation for the tevm.sh family of documentation sites.
+ * Every entry below is a live site.
+ */
+
+export const FAMILY = [
+ { name: 'tevm.sh', url: 'https://tevm.sh', desc: 'The Ethereum toolkit — start here' },
+ { name: 'contract.tevm.sh', url: 'https://contract.tevm.sh', desc: 'Typesafe contract bindings' },
+ { name: 'bundler.tevm.sh', url: 'https://bundler.tevm.sh', desc: 'Import Solidity from JS' },
+ { name: 'cli.tevm.sh', url: 'https://cli.tevm.sh', desc: 'Command line interface' },
+ { name: 'test.tevm.sh', url: 'https://test.tevm.sh', desc: 'Contract testing utilities' },
+ { name: 'logger.tevm.sh', url: 'https://logger.tevm.sh', desc: 'Structured logging' },
+ { name: 'ethers.tevm.sh', url: 'https://ethers.tevm.sh', desc: 'ethers.js integration' },
+ { name: 'mud.tevm.sh', url: 'https://mud.tevm.sh', desc: 'MUD framework integration' },
+ { name: 'examples.tevm.sh', url: 'https://examples.tevm.sh', desc: 'End-to-end examples' },
+ { name: 'voltaire.tevm.sh', url: 'https://voltaire.tevm.sh', desc: 'Ethereum primitives in Zig' },
+ {
+ name: 'guillotine.tevm.sh',
+ url: 'https://guillotine.tevm.sh',
+ desc: 'High-performance EVM interpreter',
+ },
+ { name: 'mini.tevm.sh', url: 'https://mini.tevm.sh', desc: 'Minimal spec-faithful EVM' },
+ { name: 'zevm.tevm.sh', url: 'https://zevm.tevm.sh', desc: 'Zig Ethereum client (this site)' },
+]
+
+const CURRENT = 'zevm.tevm.sh'
+
+export function FamilyNav() {
+ return (
+
+ Explore the tevm family
+
+
+ )
+}
diff --git a/site/components/Hero.tsx b/site/components/Hero.tsx
new file mode 100644
index 0000000..1b2c732
--- /dev/null
+++ b/site/components/Hero.tsx
@@ -0,0 +1,45 @@
+'use client'
+
+import { useState } from 'react'
+
+const INSTALL = 'npm install @evmts/zevm@beta'
+
+export function Hero() {
+ const [copied, setCopied] = useState(false)
+
+ const copy = () => {
+ navigator.clipboard?.writeText(INSTALL)
+ setCopied(true)
+ setTimeout(() => setCopied(false), 1500)
+ }
+
+ return (
+
+
Ethereum client · written in Zig
+
ZEVM
+
+ A writable trusted dev node and a proof-backed light client in one binary — shipped as a
+ CLI, a C ABI, and a Node.js addon with prebuilt binaries for macOS, Linux and Windows.
+
+
+
+ {INSTALL}
+
+ {copied ? 'copied' : 'copy'}
+
+
+
+
+
+ )
+}
diff --git a/site/components/PlatformPicker.tsx b/site/components/PlatformPicker.tsx
new file mode 100644
index 0000000..e9f1608
--- /dev/null
+++ b/site/components/PlatformPicker.tsx
@@ -0,0 +1,189 @@
+'use client'
+
+import { useEffect, useState } from 'react'
+
+/**
+ * Interactive platform-support picker.
+ *
+ * ZEVM ships one prebuilt native package per platform under `npm/platforms`,
+ * wired into `@evmts/zevm` as optionalDependencies. This mirrors that table so a
+ * reader can answer "is my machine supported, and which package do I get?"
+ * without cross-referencing package.json files.
+ *
+ * Source of truth: the package.json of each npm/platforms directory.
+ */
+
+type Target = {
+ os: string
+ arch: string
+ libc?: string
+ pkg: string
+}
+
+const TARGETS: Target[] = [
+ { os: 'darwin', arch: 'arm64', pkg: '@evmts/zevm-darwin-arm64' },
+ { os: 'darwin', arch: 'x64', pkg: '@evmts/zevm-darwin-x64' },
+ { os: 'linux', arch: 'arm64', libc: 'glibc', pkg: '@evmts/zevm-linux-arm64-gnu' },
+ { os: 'linux', arch: 'arm64', libc: 'musl', pkg: '@evmts/zevm-linux-arm64-musl' },
+ { os: 'linux', arch: 'x64', libc: 'glibc', pkg: '@evmts/zevm-linux-x64-gnu' },
+ { os: 'linux', arch: 'x64', libc: 'musl', pkg: '@evmts/zevm-linux-x64-musl' },
+ { os: 'win32', arch: 'arm64', pkg: '@evmts/zevm-win32-arm64-msvc' },
+ { os: 'win32', arch: 'ia32', pkg: '@evmts/zevm-win32-ia32-msvc' },
+ { os: 'win32', arch: 'x64', pkg: '@evmts/zevm-win32-x64-msvc' },
+]
+
+const OS_LABELS: Record = {
+ darwin: 'macOS',
+ linux: 'Linux',
+ win32: 'Windows',
+}
+
+const ARCHES = ['arm64', 'x64', 'ia32']
+const LIBCS = ['glibc', 'musl']
+
+function find(os: string, arch: string, libc: string) {
+ return TARGETS.find(
+ (t) => t.os === os && t.arch === arch && (os === 'linux' ? t.libc === libc : true),
+ )
+}
+
+/** Best-effort guess of the visitor's platform from the UA string. */
+function guess(): { os: string; arch: string } {
+ if (typeof navigator === 'undefined') return { os: 'darwin', arch: 'arm64' }
+ const ua = navigator.userAgent
+ const os = /Win/.test(ua) ? 'win32' : /Linux|Android/.test(ua) ? 'linux' : 'darwin'
+ const arch = /arm|aarch64|Mac OS X/.test(ua) && !/Intel Mac/.test(ua) ? 'arm64' : 'x64'
+ return { os, arch }
+}
+
+export function PlatformPicker() {
+ const [os, setOs] = useState('darwin')
+ const [arch, setArch] = useState('arm64')
+ const [libc, setLibc] = useState('glibc')
+ const [copied, setCopied] = useState(false)
+
+ useEffect(() => {
+ const g = guess()
+ setOs(g.os)
+ setArch(g.arch)
+ }, [])
+
+ // Windows has no arm64/x64/ia32 gap, but arm64 is absent on Windows ia32 etc.
+ // Keep the selected arch valid whenever the OS changes.
+ useEffect(() => {
+ if (!TARGETS.some((t) => t.os === os && t.arch === arch)) {
+ const first = TARGETS.find((t) => t.os === os)
+ if (first) setArch(first.arch)
+ }
+ }, [os, arch])
+
+ const match = find(os, arch, libc)
+ const install = 'npm install @evmts/zevm@beta'
+
+ const copy = () => {
+ navigator.clipboard?.writeText(install)
+ setCopied(true)
+ setTimeout(() => setCopied(false), 1500)
+ }
+
+ return (
+
+
+
+
+ Operating system
+
+
+ {Object.keys(OS_LABELS).map((value) => (
+ setOs(value)}
+ >
+ {OS_LABELS[value]}
+
+ ))}
+
+
+
+
+
+ Architecture
+
+
+ {ARCHES.map((value) => {
+ const available = TARGETS.some((t) => t.os === os && t.arch === value)
+ return (
+ setArch(value)}
+ >
+ {value}
+
+ )
+ })}
+
+
+
+ {os === 'linux' && (
+
+
+ libc
+
+
+ {LIBCS.map((value) => (
+ setLibc(value)}
+ >
+ {value}
+
+ ))}
+
+
+ )}
+
+
+
+ {match ? (
+ <>
+
+ Prebuilt binary
+ {match.pkg}
+
+
+ {install}
+
+ {copied ? 'copied' : 'copy'}
+
+
+
+ npm resolves {match.pkg} automatically through the{' '}
+ optionalDependencies of @evmts/zevm. Nothing is compiled at
+ install time.
+
+ >
+ ) : (
+ <>
+
+ No prebuilt binary
+
+
+ This combination has no published package. Build the binary yourself — see{' '}
+ Building From Source .
+
+ >
+ )}
+
+
+ )
+}
diff --git a/site/public/favicon.svg b/site/public/favicon.svg
new file mode 100644
index 0000000..654fd43
--- /dev/null
+++ b/site/public/favicon.svg
@@ -0,0 +1,4 @@
+
+
+ Z
+
diff --git a/site/public/tevm-logo-dark.png b/site/public/tevm-logo-dark.png
new file mode 100644
index 0000000..7fea34a
Binary files /dev/null and b/site/public/tevm-logo-dark.png differ
diff --git a/site/public/tevm-logo-light.png b/site/public/tevm-logo-light.png
new file mode 100644
index 0000000..fc75408
Binary files /dev/null and b/site/public/tevm-logo-light.png differ
diff --git a/site/src/pages/_root.css b/site/src/pages/_root.css
new file mode 100644
index 0000000..6820b4d
--- /dev/null
+++ b/site/src/pages/_root.css
@@ -0,0 +1,343 @@
+/*
+ * Shared tevm.sh family theme tokens.
+ *
+ * Matched against the live sibling sites (contract.tevm.sh, bundler.tevm.sh),
+ * which render with --vocs-color-accent:#0085FF and the Geist / Geist Mono
+ * type stack. Keep this file in sync with the family rather than diverging.
+ */
+
+:root {
+ --tevm-accent: #0085ff;
+ --tevm-accent-hover: #339dff;
+ --tevm-zig: #f7a41d;
+
+ /*
+ * Vocs derives --vocs-font-sans / --vocs-font-mono from these, and sets
+ * `color-scheme` on [data-vocs-theme], so light-dark() below resolves
+ * correctly for BOTH the system preference and the explicit theme toggle.
+ */
+ --vocs-font-family: 'Geist', ui-sans-serif, system-ui, sans-serif;
+ --vocs-font-family-mono: 'Geist Mono', ui-monospace, SFMono-Regular, Menlo, monospace;
+
+ --tevm-font-sans: var(--vocs-font-sans, var(--vocs-font-family));
+ --tevm-font-mono: var(--vocs-font-mono, var(--vocs-font-family-mono));
+
+ --tevm-surface: light-dark(#ffffff, #17181c);
+ --tevm-surface-2: light-dark(#f7f8fa, #1e2027);
+ --tevm-border: light-dark(#e2e6ec, #2c2f38);
+ --tevm-text: light-dark(#1b1b1f, #f2f3f5);
+ --tevm-text-dim: light-dark(#5c6370, #9aa1ad);
+ --tevm-hero-glow: light-dark(rgba(0, 133, 255, 0.1), rgba(0, 133, 255, 0.16));
+ --tevm-ok: light-dark(#0f9d58, #3ecf8e);
+ --tevm-danger: light-dark(#d93025, #ff6b6b);
+}
+
+/* ---------------------------------------------------------------- hero --- */
+
+.tevm-hero {
+ border: 1px solid var(--tevm-border);
+ border-radius: 14px;
+ background:
+ radial-gradient(120% 130% at 0% 0%, var(--tevm-hero-glow), transparent 62%),
+ var(--tevm-surface-2);
+ padding: 32px 28px;
+ margin: 0 0 40px;
+}
+
+.tevm-hero__eyebrow {
+ display: inline-flex;
+ align-items: center;
+ gap: 8px;
+ font-family: var(--tevm-font-mono);
+ font-size: 12px;
+ letter-spacing: 0.08em;
+ text-transform: uppercase;
+ color: var(--tevm-text-dim);
+ margin: 0 0 14px;
+}
+
+.tevm-hero__eyebrow::before {
+ content: '';
+ width: 8px;
+ height: 8px;
+ border-radius: 50%;
+ background: var(--tevm-zig);
+}
+
+.tevm-hero__title {
+ font-size: clamp(30px, 5vw, 44px);
+ line-height: 1.1;
+ font-weight: 600;
+ letter-spacing: -0.02em;
+ margin: 0 0 12px;
+ color: var(--tevm-text);
+}
+
+.tevm-hero__tagline {
+ font-size: 17px;
+ line-height: 1.55;
+ color: var(--tevm-text-dim);
+ max-width: 62ch;
+ margin: 0 0 22px;
+}
+
+.tevm-hero__install {
+ display: flex;
+ align-items: stretch;
+ gap: 0;
+ border: 1px solid var(--tevm-border);
+ border-radius: 8px;
+ background: var(--tevm-surface);
+ overflow: hidden;
+ max-width: 420px;
+}
+
+.tevm-hero__install code {
+ flex: 1;
+ font-family: var(--tevm-font-mono);
+ font-size: 14px;
+ padding: 11px 14px;
+ color: var(--tevm-text);
+ white-space: nowrap;
+ overflow-x: auto;
+ background: none;
+ border: none;
+}
+
+.tevm-copy {
+ border: none;
+ border-left: 1px solid var(--tevm-border);
+ background: var(--tevm-surface-2);
+ color: var(--tevm-text-dim);
+ font-family: var(--tevm-font-mono);
+ font-size: 12px;
+ padding: 0 14px;
+ cursor: pointer;
+ transition: color 0.15s, background 0.15s;
+}
+
+.tevm-copy:hover {
+ color: var(--tevm-accent);
+ background: var(--tevm-surface);
+}
+
+.tevm-hero__ctas {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 10px;
+ margin-top: 20px;
+}
+
+.tevm-hero__cta {
+ display: inline-block;
+ padding: 9px 16px;
+ border-radius: 8px;
+ font-size: 14px;
+ font-weight: 500;
+ text-decoration: none;
+ border: 1px solid var(--tevm-border);
+ color: var(--tevm-text);
+}
+
+.tevm-hero__cta--primary {
+ background: var(--tevm-accent);
+ border-color: var(--tevm-accent);
+ color: #fff;
+}
+
+.tevm-hero__cta--primary:hover {
+ background: var(--tevm-accent-hover);
+ border-color: var(--tevm-accent-hover);
+}
+
+.tevm-hero__cta:hover {
+ border-color: var(--tevm-accent);
+}
+
+/* ------------------------------------------------------- platform picker --- */
+
+.zevm-platform {
+ border: 1px solid var(--tevm-border);
+ border-radius: 12px;
+ background: var(--tevm-surface-2);
+ padding: 20px;
+ margin: 28px 0;
+}
+
+.zevm-platform__row {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 20px;
+ margin-bottom: 18px;
+}
+
+.zevm-platform__group {
+ display: flex;
+ flex-direction: column;
+ gap: 7px;
+}
+
+.zevm-platform__label {
+ font-family: var(--tevm-font-mono);
+ font-size: 11px;
+ letter-spacing: 0.08em;
+ text-transform: uppercase;
+ color: var(--tevm-text-dim);
+}
+
+.zevm-platform__options {
+ display: flex;
+ gap: 6px;
+ flex-wrap: wrap;
+}
+
+.zevm-chip {
+ font-family: var(--tevm-font-mono);
+ font-size: 13px;
+ padding: 6px 12px;
+ border-radius: 999px;
+ border: 1px solid var(--tevm-border);
+ background: var(--tevm-surface);
+ color: var(--tevm-text-dim);
+ cursor: pointer;
+ transition: all 0.15s;
+}
+
+.zevm-chip:hover:not(:disabled) {
+ border-color: var(--tevm-accent);
+ color: var(--tevm-text);
+}
+
+.zevm-chip[aria-pressed='true'] {
+ background: var(--tevm-accent);
+ border-color: var(--tevm-accent);
+ color: #fff;
+}
+
+.zevm-chip:disabled {
+ opacity: 0.35;
+ cursor: not-allowed;
+ text-decoration: line-through;
+}
+
+.zevm-platform__result {
+ border-top: 1px solid var(--tevm-border);
+ padding-top: 16px;
+}
+
+.zevm-platform__pkg {
+ display: flex;
+ align-items: center;
+ gap: 10px;
+ flex-wrap: wrap;
+ margin-bottom: 10px;
+}
+
+.zevm-platform__pkg code {
+ font-family: var(--tevm-font-mono);
+ font-size: 14px;
+ color: var(--tevm-text);
+ background: var(--tevm-surface);
+ border: 1px solid var(--tevm-border);
+ border-radius: 6px;
+ padding: 4px 10px;
+}
+
+.zevm-badge {
+ font-family: var(--tevm-font-mono);
+ font-size: 11px;
+ padding: 3px 8px;
+ border-radius: 999px;
+ border: 1px solid var(--tevm-border);
+ color: var(--tevm-text-dim);
+}
+
+.zevm-badge--ok {
+ color: var(--tevm-ok);
+ border-color: currentColor;
+}
+
+.zevm-badge--none {
+ color: var(--tevm-danger);
+ border-color: currentColor;
+}
+
+.zevm-platform__note {
+ font-size: 13px;
+ line-height: 1.55;
+ color: var(--tevm-text-dim);
+ margin: 10px 0 0;
+}
+
+.zevm-platform__note a {
+ color: var(--tevm-accent);
+}
+
+/* Keep inline code in the note tight so the sentence reads as one line. */
+.zevm-platform__note code {
+ font-family: var(--tevm-font-mono);
+ font-size: 0.92em;
+ padding: 0 2px;
+ margin: 0;
+ background: none;
+ border: none;
+ color: var(--tevm-text);
+ word-spacing: normal;
+}
+
+/* ------------------------------------------------------------ family nav --- */
+
+.tevm-family {
+ border-top: 1px solid var(--tevm-border);
+ margin-top: 48px;
+ padding-top: 22px;
+}
+
+.tevm-family__title {
+ font-family: var(--tevm-font-mono);
+ font-size: 11px;
+ letter-spacing: 0.08em;
+ text-transform: uppercase;
+ color: var(--tevm-text-dim);
+ margin: 0 0 14px;
+}
+
+.tevm-family__grid {
+ display: grid;
+ grid-template-columns: repeat(auto-fill, minmax(190px, 1fr));
+ gap: 8px;
+}
+
+.tevm-family__item {
+ display: block;
+ border: 1px solid var(--tevm-border);
+ border-radius: 8px;
+ background: var(--tevm-surface-2);
+ padding: 10px 12px;
+ text-decoration: none;
+ transition: border-color 0.15s, background 0.15s;
+}
+
+.tevm-family__item:hover {
+ border-color: var(--tevm-accent);
+ background: var(--tevm-surface);
+}
+
+.tevm-family__item[aria-current='page'] {
+ border-color: var(--tevm-accent);
+}
+
+.tevm-family__name {
+ display: block;
+ font-family: var(--tevm-font-mono);
+ font-size: 13px;
+ color: var(--tevm-accent);
+}
+
+.tevm-family__desc {
+ display: block;
+ font-size: 12px;
+ line-height: 1.4;
+ color: var(--tevm-text-dim);
+ margin-top: 2px;
+}
diff --git a/site/src/pages/index.mdx b/site/src/pages/index.mdx
index 82833cc..cf60139 100644
--- a/site/src/pages/index.mdx
+++ b/site/src/pages/index.mdx
@@ -3,7 +3,11 @@ title: ZEVM
description: A Zig Ethereum client with a writable trusted dev node and a proof-backed light client.
---
-# ZEVM
+import { Hero } from '../../components/Hero'
+import { PlatformPicker } from '../../components/PlatformPicker'
+import { FamilyNav } from '../../components/FamilyNav'
+
+
ZEVM is an Ethereum client written in Zig. It runs in exactly two modes:
@@ -47,6 +51,8 @@ npm install @evmts/zevm@beta
Or build the binary from source — see [Installation](/quickstart/installation) and
[Building From Source](/guides/building-from-source).
+
+
## Start a dev node
```bash
@@ -85,3 +91,5 @@ ZEVM is pre-1.0. The first real release is `0.1.0-beta.1`; the public API and na
usable, but cross-platform packaging still wants consumer feedback. Phase-1 scope means HTTP
JSON-RPC only — no built-in TLS and no JSON-RPC authentication. Keep `--host 127.0.0.1` unless you
have put a reverse proxy in front of it.
+
+
diff --git a/site/src/pages/quickstart/installation.mdx b/site/src/pages/quickstart/installation.mdx
index 7e6f40e..be17da3 100644
--- a/site/src/pages/quickstart/installation.mdx
+++ b/site/src/pages/quickstart/installation.mdx
@@ -5,8 +5,12 @@ description: Build ZEVM from source and run it locally.
# Installation
+import { PlatformPicker } from '../../../components/PlatformPicker'
+
ZEVM can be built from source or from release artifact jobs. Source builds use Zig package-manager pins in `build.zig.zon`.
+
+
## Prerequisites
- Zig `0.15.2` or newer (`minimum_zig_version` in `build.zig.zon`)
diff --git a/site/vocs.config.ts b/site/vocs.config.ts
index 12dba8a..a3297b8 100644
--- a/site/vocs.config.ts
+++ b/site/vocs.config.ts
@@ -6,6 +6,12 @@ export default defineConfig({
'ZEVM is a Zig Ethereum client with a writable trusted dev node and a proof-backed light client, distributed as a binary, a C ABI, and a Node.js addon.',
baseUrl: 'https://zevm.tevm.sh',
rootDir: '.',
+ // Shared tevm.sh family identity — matches contract.tevm.sh / bundler.tevm.sh.
+ logoUrl: { light: '/tevm-logo-light.png', dark: '/tevm-logo-dark.png' },
+ iconUrl: '/favicon.svg',
+ // Same accent the sibling family sites render with.
+ accentColor: '#0085FF',
+ colorScheme: 'light dark',
editLink: {
pattern: 'https://github.com/evmts/zevm/edit/main/site/src/pages/:path',
text: 'Edit on GitHub',
@@ -21,6 +27,24 @@ export default defineConfig({
text: 'npm',
link: 'https://www.npmjs.com/package/@evmts/zevm',
},
+ {
+ text: 'tevm family',
+ items: [
+ { text: 'tevm.sh', link: 'https://tevm.sh' },
+ { text: 'contract', link: 'https://contract.tevm.sh' },
+ { text: 'bundler', link: 'https://bundler.tevm.sh' },
+ { text: 'cli', link: 'https://cli.tevm.sh' },
+ { text: 'test', link: 'https://test.tevm.sh' },
+ { text: 'logger', link: 'https://logger.tevm.sh' },
+ { text: 'ethers', link: 'https://ethers.tevm.sh' },
+ { text: 'mud', link: 'https://mud.tevm.sh' },
+ { text: 'examples', link: 'https://examples.tevm.sh' },
+ { text: 'voltaire', link: 'https://voltaire.tevm.sh' },
+ { text: 'guillotine', link: 'https://guillotine.tevm.sh' },
+ { text: 'mini', link: 'https://mini.tevm.sh' },
+ { text: 'zevm', link: 'https://zevm.tevm.sh' },
+ ],
+ },
],
sidebar: [
{ text: 'Introduction', link: '/' },