Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .github/workflows/hugo.yml
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,12 @@ jobs:
with:
submodules: recursive

# Hugo resolves the landingkit module (hugo.toml [module]) with the go
# command, at the version go.mod pins.
- uses: actions/setup-go@v7
with:
go-version-file: go.mod
cache: false
- name: Setup Pages
id: pages
uses: actions/configure-pages@v6
Expand Down
5 changes: 5 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
module github.com/go-webengine/go-webengine.github.io

go 1.27.3

require github.com/go-fleettools/landingkit v0.6.0
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
github.com/go-fleettools/landingkit v0.6.0 h1:lIlazNZ3xTWDSti/16Uo+aSUOFPhVniQaEnILbQmJuE=
github.com/go-fleettools/landingkit v0.6.0/go.mod h1:gBpbziqFcmcjtNknPjy5+AAzGZ5L+pPr9mIJZHpDlZc=
26 changes: 26 additions & 0 deletions hugo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@ baseURL = "https://go-webengine.github.io/"
title = "go-webengine"
disableKinds = ["taxonomy", "term", "RSS", "sitemap", "404"]

# The design comes from the landing kit, a Hugo module pinned in go.mod.
[module]
[[module.imports]]
path = "github.com/go-fleettools/landingkit"

[markup.goldmark.renderer]
unsafe = true

Expand Down Expand Up @@ -50,3 +55,24 @@ disableKinds = ["taxonomy", "term", "RSS", "sitemap", "404"]
title = "Remaining levers"
state = "planned"
text = "Closing fidelity residuals (list markers, icon fonts, conic-gradient/filter/mask, SVG filters) and the perf gap on large computed pages. Diminishing returns — mean SSIM ≈ 0.69, example.com at 0.954."

[params.brand]
# Generated by go-fleettools/landingkit from static/img/logo.svg: the colours are
# READ from the logo's gradient, never chosen. Do not add other keys:
# neutrals (backgrounds, surfaces, greys) belong to the shared sheet.
# Contrast: base on white 4.88:1, bright on the dark background 9.94:1.
base = "#0079a8"
deep = "#004a66"
bright = "#22cce2"
shadow = "rgba(0,121,168,.7)"
glow = "rgba(0,121,168,.6)"
wash = "rgba(0,121,168,.10)"
washDeep = "rgba(0,74,102,.06)"
chipBg = "#e4f9fc"
chipBorder = "#bdf0f6"
chipFg = "#004a66"
chipBgDark = "rgba(34,204,226,.14)"
chipBorderDark = "rgba(34,204,226,.32)"
chipFgDark = "#85e3ef"
statsEnd = "#caf3f8"
bandText = "#dcf7fa"
9 changes: 9 additions & 0 deletions kit.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
//go:build tools

// This file never builds. A Hugo site has no Go code, so `go mod tidy` --
// which Renovate runs after every bump -- would find nothing that uses
// github.com/go-fleettools/landingkit and delete its requirement from go.mod.
// Importing it here keeps it. See the landingkit README.
package site

import _ "github.com/go-fleettools/landingkit"
177 changes: 57 additions & 120 deletions layouts/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,119 +3,68 @@
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
{{/* Dark Reader repaints pages that already draw their own dark theme, as this one does: ask it to stand aside. */}}
<meta name="darkreader-lock">
<title>{{ .Site.Title }} — pure-Go headless web engine</title>
<meta name="description" content="A pure-Go, CGO_ENABLED=0 headless web engine: fetch a URL, parse HTML into a DOM, apply a real CSS cascade, run the page's JavaScript, lay out a full box model (flex/grid/tables/position), and paint anti-aliased text, gradients, images and SVG to an image — no Chromium, no cgo. The rendering core of the go-webengine browserproxy remote-browser service.">
<link rel="icon" type="image/svg+xml" href="/favicon.svg">
{{ partial "styles.html" . }}
{{/* The template's own rules for what <main> holds, kept for it alone (class legacy),
its colours read from the kit's tokens. Written by landingmigrate. */}}
<style>
:root {
--bg: #ffffff; --fg: #1b1f24; --muted: #57606a; --card: #f6f8fa;
--border: #d0d7de; --accent: #0079a8; --accent-fg: #ffffff;
--ship: #0079a8; --plan: #9a6700;
}
@media (prefers-color-scheme: dark) {
:root:not([data-theme]) {
--bg: #0d1117; --fg: #e6edf3; --muted: #9198a1; --card: #161b22;
--border: #30363d; --accent: #22cce2; --accent-fg: #0d1117;
--ship: #22cce2; --plan: #d29922;
}
}
:root[data-theme="dark"] {
--bg: #0d1117; --fg: #e6edf3; --muted: #9198a1; --card: #161b22;
--border: #30363d; --accent: #22cce2; --accent-fg: #0d1117;
--ship: #22cce2; --plan: #d29922;
}
:root[data-theme="light"] {
--bg: #ffffff; --fg: #1b1f24; --muted: #57606a; --card: #f6f8fa;
--border: #d0d7de; --accent: #0079a8; --accent-fg: #ffffff;
--ship: #0079a8; --plan: #9a6700;
}
.theme-toggle {
position: fixed; top: 14px; right: 14px; z-index: 10;
font: inherit; font-size: .8rem; cursor: pointer; padding: 6px 12px;
color: var(--fg); background: var(--card); border: 1px solid var(--border); border-radius: 999px;
}
.theme-toggle:hover { border-color: var(--accent); }
* { box-sizing: border-box; }
body {
margin: 0; background: var(--bg); color: var(--fg);
font: 16px/1.6 -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif;
}
.wrap { max-width: 980px; margin: 0 auto; padding: 0 20px; }
header { padding: 72px 0 32px; text-align: center; }
.logo { width: 88px; height: 88px; margin: 0 auto 18px; display: block; }
h1 { font-size: 2.6rem; margin: 0 0 .3em; letter-spacing: -.02em; }
.tagline { font-size: 1.2rem; color: var(--muted); margin: 0 auto; max-width: 700px; }
.pills { margin: 22px 0 0; }
.pill {
display: inline-block; margin: 4px; padding: 3px 10px; font-size: .82rem;
color: var(--muted); background: var(--card); border: 1px solid var(--border); border-radius: 999px;
}
.cta { margin: 30px 0 0; }
.btn {
display: inline-block; padding: 10px 18px; margin: 4px; border-radius: 8px; text-decoration: none;
font-weight: 600; border: 1px solid var(--border);
}
.btn .icon{display:inline-block;vertical-align:-0.15em;margin-right:.45em}
.btn.primary { background: var(--accent); color: var(--accent-fg); border-color: var(--accent); }
.btn.ghost { color: var(--fg); background: transparent; }
.lead { color: var(--fg); max-width: 820px; margin: 8px auto 0; text-align: center; }
.lead code { background: var(--card); border: 1px solid var(--border); border-radius: 6px; padding: 1px 6px; }
h2.section { text-align: center; margin: 44px 0 4px; font-size: 1.4rem; }
pre.pipe {
max-width: 820px; margin: 14px auto 0; overflow-x: auto; background: var(--card);
.legacy{--fg:var(--ink);--muted:var(--slate);--card:var(--bg-soft);--border:var(--line);--ship:#0079a8;--plan:#9a6700}
@media(prefers-color-scheme:dark){:root:not([data-theme]) .legacy{--ship:#22cce2;--plan:#d29922}}
:root[data-theme="dark"] .legacy{--ship:#22cce2;--plan:#d29922}
.legacy .theme-toggle:hover{border-color: var(--accent);}
.legacy .lead{color: var(--fg); max-width: 820px; margin: 8px auto 0; text-align: center;}
.legacy h2.section{text-align: center; margin: 44px 0 4px; font-size: 1.4rem;}
.legacy pre.pipe{max-width: 820px; margin: 14px auto 0; overflow-x: auto; background: var(--card);
border: 1px solid var(--border); border-radius: 12px; padding: 16px 18px;
font: .82rem/1.5 ui-monospace, SFMono-Regular, Menlo, monospace; color: var(--fg);
}
.grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 16px; padding: 16px 0 8px; }
.card { background: var(--card); border: 1px solid var(--border); border-radius: 12px; padding: 18px 20px; display: flex; flex-direction: column; }
.card h3 { margin: 0 0 .35em; font-size: 1.2rem; display: flex; align-items: center; justify-content: space-between; }
.card h3 a { text-decoration: none; }
.card p { margin: 0 0 .8em; color: var(--fg); font-size: .95rem; }
.card .badges { margin-top: auto; }
.card .badges img { height: 20px; vertical-align: middle; }
.type { font-size: .72rem; font-weight: 700; text-transform: uppercase; letter-spacing: .04em; padding: 2px 8px; border-radius: 999px; color: var(--accent); border: 1px solid var(--accent); }
.phases { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: 14px; padding: 16px 0 8px; }
.phase { background: var(--card); border: 1px solid var(--border); border-radius: 12px; padding: 16px 18px; }
.phase h3 { margin: 0 0 .3em; font-size: 1.05rem; }
.phase .state { font-size: .68rem; font-weight: 700; text-transform: uppercase; letter-spacing: .05em; padding: 2px 8px; border-radius: 999px; margin-left: 6px; }
.phase .state.shipping { color: var(--accent-fg); background: var(--ship); }
.phase .state.planned { color: var(--plan); border: 1px solid var(--plan); }
.phase p { margin: .4em 0 0; color: var(--muted); font-size: .9rem; }
.principles { color: var(--muted); text-align: center; padding: 10px 0 0; max-width: 820px; margin: 0 auto; }
footer { color: var(--muted); text-align: center; padding: 40px 0 56px; font-size: .9rem; }
a { color: var(--accent); }
font: .82rem/1.5 ui-monospace, SFMono-Regular, Menlo, monospace; color: var(--fg);}
.legacy .grid{display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 16px; padding: 16px 0 8px;}
.legacy .card{background: var(--card); border: 1px solid var(--border); border-radius: 12px; padding: 18px 20px; display: flex; flex-direction: column;}
.legacy .card h3{margin: 0 0 .35em; font-size: 1.2rem; display: flex; align-items: center; justify-content: space-between;}
.legacy .card h3 a{text-decoration: none;}
.legacy .card p{margin: 0 0 .8em; color: var(--fg); font-size: .95rem;}
.legacy .card .badges{margin-top: auto;}
.legacy .card .badges img{height: 20px; vertical-align: middle;}
.legacy .type{font-size: .72rem; font-weight: 700; text-transform: uppercase; letter-spacing: .04em; padding: 2px 8px; border-radius: 999px; color: var(--accent); border: 1px solid var(--accent);}
.legacy .phases{display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: 14px; padding: 16px 0 8px;}
.legacy .phase{background: var(--card); border: 1px solid var(--border); border-radius: 12px; padding: 16px 18px;}
.legacy .phase h3{margin: 0 0 .3em; font-size: 1.05rem;}
.legacy .phase .state{font-size: .68rem; font-weight: 700; text-transform: uppercase; letter-spacing: .05em; padding: 2px 8px; border-radius: 999px; margin-left: 6px;}
.legacy .phase .state.shipping{color: var(--accent-fg); background: var(--ship);}
.legacy .phase .state.planned{color: var(--plan); border: 1px solid var(--plan);}
.legacy .phase p{margin: .4em 0 0; color: var(--muted); font-size: .9rem;}
.legacy .principles{color: var(--muted); text-align: center; padding: 10px 0 0; max-width: 820px; margin: 0 auto;}
</style>
<link rel="icon" type="image/svg+xml" href="/favicon.svg">
<script>
(function(){var m=localStorage.getItem('theme');if(m&&m!=='system')document.documentElement.setAttribute('data-theme',m);})();
</script>
</head>
<body>
<button id="theme-toggle" class="theme-toggle" type="button" aria-label="Toggle light, dark or system theme"><span id="theme-label">🖥️ System</span></button>
<header class="wrap">
<img class="logo" src="/img/logo.svg" alt="go-webengine logo" width="88" height="88">
<h1>go-webengine</h1>
<p class="tagline">A pure-Go, <strong>headless web engine</strong> — give it a URL,
<body class="lm">
<header class="nav"><div class="wrap">
<a class="brand" href="/"><img class="glyph" src="/img/logo.svg" width="32" height="32" alt="go-webengine logo"> {{ .Site.Title }}</a>
{{ partial "theme-toggle.html" . }}
</div></header>
{{ partial "fork-ribbon.html" "https://github.com/go-webengine" }}

<div class="hero"><div class="wrap">
<div>
<h1 class="head">go-webengine</h1>
<p class="sub">A pure-Go, <strong>headless web engine</strong> — give it a URL,
get back an <strong>image of the page</strong>. HTML/CSS layout and paint with
<strong>no Chromium and no cgo</strong>.</p>
<div class="pills">
<span class="pill">CGO_ENABLED=0</span>
<span class="pill">no Chromium · no host web view</span>
<span class="pill">JavaScript (goja) + real DOM</span>
<span class="pill">flex · grid · tables · position</span>
<span class="pill">gradients · box-shadow · SVG</span>
<span class="pill">amd64 · arm64 · riscv64 · loong64 · ppc64le · s390x</span>
<span class="pill">ratchet coverage gate</span>
<span class="pill">measured vs headless Chrome</span>
</div>
<div class="cta">
<a class="btn primary" href="https://go-webengine.github.io/docs/">{{ partial "icons/book.html" . }}Documentation</a>
<a class="btn ghost" href="https://github.com/go-webengine/engine">{{ partial "icons/mark-github.html" . }}GitHub</a>
<div class="cta">
<a class="btn btn-primary" href="https://go-webengine.github.io/docs/">{{ partial "icons/book.html" . }}Documentation</a>
<a class="btn btn-ghost" href="https://github.com/go-webengine/engine">{{ partial "icons/github.html" . }}GitHub</a>
</div>
<div class="trust">{{ if not (partial "mantra-on.html" $) }}<span class="pill">CGO_ENABLED=0</span>{{ end }}<span class="pill">no Chromium · no host web view</span><span class="pill">JavaScript (goja) + real DOM</span><span class="pill">flex · grid · tables · position</span><span class="pill">gradients · box-shadow · SVG</span><span class="pill">amd64 · arm64 · riscv64 · loong64 · ppc64le · s390x</span><span class="pill">ratchet coverage gate</span><span class="pill">measured vs headless Chrome</span></div>
</div>
</header>
{{ partial "hero-carousel.html" . }}
</div></div>

{{ with .Site.Params.repos }}<div class="strip"><div class="wrap">
<span class="lead-line">One organisation, <b>{{ len . }}</b> repositories</span>
<div class="mods">{{ range . }}<a href="#repos">{{ .name }}</a>{{ end }}</div>
</div></div>{{ end }}

<main class="wrap">
<main id="repos" class="legacy"><div class="wrap">
<p class="lead">Modern "render a web page" tooling almost always means shipping a whole
browser &mdash; Chromium via CDP, a WebView, cgo bindings. <strong>go-webengine renders in
pure Go.</strong> It fetches the page, builds an owned DOM over
Expand Down Expand Up @@ -159,7 +108,7 @@ <h3>
<div class="badges">
<a href="https://github.com/go-webengine/{{ .name }}/actions/workflows/ci.yml"><img src="https://github.com/go-webengine/{{ .name }}/actions/workflows/ci.yml/badge.svg" alt="CI"></a>
<img src="https://img.shields.io/badge/coverage%20gate-css%2099.5%20%C2%B7%20layout%2Fpaint%2Fpaginate%20100%20%C2%B7%20dom%2097.4-brightgreen" alt="coverage gate">
<img src="https://img.shields.io/badge/License-BSD--3--Clause-blue.svg" alt="BSD-3-Clause">
{{ if not (partial "mantra-on.html" $) }}<img src="https://img.shields.io/badge/License-BSD--3--Clause-blue.svg" alt="BSD-3-Clause">{{ end }}
</div>
{{ end }}
</article>
Expand All @@ -180,22 +129,10 @@ <h3>Phase {{ .n }} &middot; {{ .title }}<span class="state {{ .state }}">{{ .sta
64-bit targets. The pure-logic packages (cascade, line-breaker, box metrics, DOM) are
asserted at exact geometry behind a ratchet coverage gate; a committed golden PNG covers the
offline paint path. Prior pure-Go browser work (opossum / mycel) was studied but not built
on &mdash; its layout core is stub-quality; see the survey. BSD-3-Clause throughout.</p>
</main>
on &mdash; its layout core is stub-quality; see the survey. {{ if not (partial "mantra-on.html" $) }}BSD-3-Clause throughout.{{ end }}</p>
</div></main>

<footer class="wrap">
BSD-3-Clause · <a href="https://github.com/go-webengine">github.com/go-webengine</a> ·
part of the <a href="https://github.com/wasmdesk">wasmdesk</a> roadmap
</footer>
<script>
(function(){
var order=['system','light','dark'], root=document.documentElement;
var labels={system:'🖥️ System',light:'☀️ Light',dark:'🌙 Dark'};
function apply(m){ if(m==='system'){root.removeAttribute('data-theme');}else{root.setAttribute('data-theme',m);} localStorage.setItem('theme',m); var el=document.getElementById('theme-label'); if(el)el.textContent=labels[m]; }
var mode=localStorage.getItem('theme')||'system'; apply(mode);
var btn=document.getElementById('theme-toggle');
if(btn) btn.addEventListener('click',function(){ mode=order[(order.indexOf(mode)+1)%order.length]; apply(mode); });
})();
</script>
<footer><div class="wrap"><span>{{ if not (partial "mantra-on.html" $) }}BSD-3-Clause · {{ end }}<a href="https://github.com/go-webengine">github.com/go-webengine</a> ·
part of the <a href="https://github.com/wasmdesk">wasmdesk</a> roadmap</span></div></footer>
</body>
</html>
7 changes: 7 additions & 0 deletions layouts/partials/hero-terminal.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<div class="term">
<div class="bar"><i style="background:#ff5f57"></i><i style="background:#febc2e"></i><i style="background:#28c840"></i><span class="t">zsh</span></div>
<pre>{{ with .Site.Params.repos }}{{ range first 6 . }}{{ with or .pkg (strings.TrimPrefix "https://" (.url | default "")) (printf "github.com/go-webengine/%s" .name) }}<span class="c-cmd">$ go get</span> {{ . }}@latest
{{ end }}{{ end }}{{ else }}<span class="c-cmd">$ go get</span> github.com/go-webengine/engine@latest
<span class="c-cmd">$ go get</span> github.com/go-webengine/browserproxy@latest
{{ end }}</pre>
</div>
21 changes: 0 additions & 21 deletions layouts/partials/icons/OCTICONS-LICENSE

This file was deleted.

5 changes: 0 additions & 5 deletions layouts/partials/icons/book.html

This file was deleted.

Loading
Loading