Mljr Web
The portfolio you are looking at — built from scratch in Go using gomponents for type-safe HTML, Datastar for reactive updates without JavaScript frameworks, and Tailwind v4 for styling. A neo-brutalist four-theme component library powers over 170 UI components rendered server-side. The full monorepo includes a data pipeline, asset optimizer, and i18n support in EN and DE.




This is the monorepo behind the site you're reading. Every component — gauges, the skills radar, the regex lab, this very project detail page — is a Go function returning HTML via gomponents; there's no JSX, no client-side virtual DOM, and no npm build step in the request path. Datastar (a single ~14 kB script) wires up reactivity by reading data-* attributes already present in the server-rendered HTML, and SSE patches fragments in place for things like the homelab panel without a page reload. The data layer is a separate Go module (mljr-data) that runs as a scheduled job: it hits the GitHub GraphQL API for contribution stats and language breakdowns, scrapes Strava and LinkedIn where APIs allow it, and writes one site-data.json that the homepage binary embeds as a build-time fallback and can also hot-reload from disk in production. Four themes × two modes are pure CSS custom-property swaps — no separate component variants to maintain.
Architecture
graph TD Generator["mljr-data generator
(scheduled job)"] -->|GitHub GraphQL, Strava, LinkedIn| JSON[("site-data.json")] JSON -->|embedded fallback + hot reload| Binary["homepage Go binary"] Binary -->|gomponents render| HTML["Server-rendered HTML"] HTML -->|data-* attributes| Datastar["Datastar (~14kB)"] Binary -->|SSE PatchElements| Datastar Datastar -->|in-place DOM patch| HTML
Under the hood
The skills radar is plain trigonometry rendered to an SVG polygon at request time — no charting library.
// Radar chart: axis i sits at angle 2πi/n, starting at
// 12 o'clock. Values scale along the spoke; the series
// becomes a single <polygon>.
angle := func(i int) float64 {
return float64(i)*2*math.Pi/float64(n) - math.Pi/2
}
pt := func(radius float64, i int) (float64, float64) {
a := angle(i)
return cx + radius*math.Cos(a), cy + radius*math.Sin(a)
}
pts := make([]string, len(p.Axes))
for i := range p.Axes {
scaled := (s.Values[i] / p.Max) * r
x, y := pt(scaled, i)
pts[i] = fmt.Sprintf("%.1f,%.1f", x, y)
}Live updates without a framework: one attribute on the section, one SSE handler, Datastar morphs the panel in place.
// One attribute on the section…
h.Section(
h.ID("homelab"),
g.Attr("data-on-interval__duration.60s", "@get('/api/homelab')"),
)
// …one SSE handler on the server.
e.GET("/api/homelab", func(c echo.Context) error {
sse := datastar.NewSSE(c.Response().Writer, c.Request())
return sse.PatchElements(
web.RenderToString(pages.HomelabPanel(snapshot())),
)
})