WithLanguage("go"). Comments are dimmed-italic, keywords
use the primary color, types use the secondary, and string and numeric literals use the tertiary.package main
import (
"fmt"
"net/http"
)
// HandleHello greets the requester.
func HandleHello(w http.ResponseWriter, r *http.Request) {
name := r.URL.Query().Get("name")
if name == "" {
name = "world"
}
fmt.Fprintf(w, "Hello, %s!", name)
}
func main() {
http.HandleFunc("/hello", HandleHello)
http.ListenAndServe(":8080", nil)
}
@dataclass), built-ins (print,
sorted, len) and class names each get their own token class. Line
numbers are enabled here via WithLineNumbers(true). 1from dataclasses import dataclass
2from typing import Iterable
3
4
5@dataclass
6class State:
7 abbrev: str
8 name: str
9 population: int
10
11 def density(self, land_sq_mi: float) -> float:
12 """People per square mile."""
13 return self.population / land_sq_mi
14
15
16def by_population(states: Iterable[State]) -> list[State]:
17 return sorted(states, key=lambda s: s.population, reverse=True)
18
19
20if __name__ == "__main__":
21 sample = [
22 State("CA", "California", 39_538_223),
23 State("TX", "Texas", 29_145_505),
24 State("FL", "Florida", 21_538_187),
25 ]
26 for s in by_population(sample):
27 print(f"{s.abbrev}: {s.population:,}")
<style>, and
embedded JavaScript inside <script> all get their respective lexers chained
together by Chroma's HTML lexer. This block is capped at 10 visible rows via WithMaxRows(10); scroll the inner pane to see the rest.<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Hello</title>
<style>
body { font-family: sans-serif; color: #222; }
.greeting { font-size: 2rem; font-weight: 600; }
</style>
</head>
<body>
<h1 class="greeting">Hello, world!</h1>
<p>This is a <a href="/about">simple page</a>.</p>
<script>
document.querySelector(".greeting").addEventListener("click", () => {
alert("clicked");
});
</script>
</body>
</html>
Declarative typed widgets in the shape of SwiftUI, Jetpack Compose, or Flutter — except the renderer is your net/http server. BESPA composes a tree of Go structs, ships HTML to the browser, and quietly swaps in updated fragments as state changes. No JavaScript framework. No build step. No bundler. Material Design 3 is the default.
func(w http.ResponseWriter, r *http.Request) {
wf.Page().Add("Hello, World!").Draw(w, r)
}
package main
import (
"net/http"
"github.com/microbus-io/bespa"
"github.com/microbus-io/bespa/widget"
)
var wf = bespa.DefaultFactory{}
func handleHome(w http.ResponseWriter, r *http.Request) {
state := wf.StateOf(r)
page := wf.Page().Add(
wf.AppBar("Hello"),
wf.Form().Add(
wf.InputText("name", "").
WithPlaceholder("Your name").
WithAutoSubmit(true),
),
wf.HeadlineMedium("Hello, ", state.Get("name"), "!").
HideIfEmpty(r, "name").
RedrawIfChanged(r, "name"),
)
page.Draw(w, r)
}
func main() {
http.HandleFunc("/bespa/", widget.AssetRegistry.ServeHTTP)
http.HandleFunc("/", handleHome)
http.ListenAndServe(":8080", nil)
}
http://localhost:8080.RedrawIfChanged and re-render whenever it moves. A counter looks like this:state := wf.StateOf(r)
count, _ := strconv.Atoi(state.Get("count"))
page := wf.Page().Add(
wf.AppBar("Counter"),
wf.HeadlineLarge(count).
RedrawIfChanged(r, "count"),
wf.Toolbar().AddLeft(
wf.ButtonFilled("").Add("-").
WithHref("?count=" + strconv.Itoa(count-1)).
RedrawIfChanged(r, "count"),
wf.ButtonFilled("").Add("+").
WithHref("?count=" + strconv.Itoa(count+1)).
RedrawIfChanged(r, "count"),
),
)