diff --git a/VOICE.md b/VOICE.md index bef799a..14ff931 100644 --- a/VOICE.md +++ b/VOICE.md @@ -107,6 +107,44 @@ where the choosing isn't the point. It's deliberately not in the checker. **The `ui-patterns/` heading skeleton** — `Template` / `Handler & state` / `When to use` — is a catalog template. Fix the prose under it, not the shape. +## Every heading that ends in a period has to earn one + +A heading punctuated as a sentence must *be* one: it needs a finite verb and +something doing the verb. `content/index.md` shipped four that weren't — +"A shared greeting wall, running on this page.", "The parts of that worth a +second look.", "Other things in here.", "Getting started." Those are labels. A +determiner, a noun, maybe a participle, and a full stop pretending. + +This isn't passive voice, but it reads as passive for the same reason: no finite +verb, so nothing is doing anything. The repo owner called it out as passive, and +that instinct was right about the effect even though the term doesn't fit. + +The tell is *where* it happens. Headings that make a technical claim stayed +sentences — "The button's name is the action", "The HTML rule runs again in Go", +"Two calls sync your tabs". The four fragments were all headings that orient the +reader: the demo, the section intro, the catch-all, the CTA. The register breaks +where the page navigates rather than argues, so check the connective headings +hardest. + +No checker rule for this — "lacks a finite verb" needs a parser, and a fragile +approximation gets switched off. Read each heading and ask what the verb is. + +Keep the rail label, the eyebrow and the h2 of a section saying the same thing. +When they drifted, all three were vague at once ("What's going on" / "What's +going on" / "The parts of that worth a second look.") and none named the +contents. + +## A comparison table must concede + +Every row of the landing's comparison sat under a neighbour's name and described +LiveTemplate instead — so the Alpine.js row read as a description of Alpine. Five +rows, five wins, no losses, from a library whose own eyebrow says alpha. A +comparison that loses to nobody gets discounted whole. + +Describe the neighbour, name what it does better, then say where the line falls. +Don't make every row concede-then-win in the same shape either; that cadence is +the same sales pitch in a new suit. Some rows concede and stop. + ## What the checker does `scripts/voice-check.sh` counts the tells that can be counted: emoji, diff --git a/content/index.md b/content/index.md index 5de12f8..bfc252f 100644 --- a/content/index.md +++ b/content/index.md @@ -1,6 +1,6 @@ --- title: "LiveTemplate — Build interactive web apps in Go with standard HTML templates" -description: "Write html/template and Go handlers, and the page updates itself. No SPA, no JSON API, no build step." +description: "Write an html/template and a Go handler. The server re-renders on each action and patches what changed, so app screens work without an SPA or a build step." layout: landing --- @@ -28,13 +28,12 @@ layout: landing @@ -44,7 +43,7 @@ layout: landing
Alpha · a Go library for server-rendered app screens

Build interactive web apps in Go with standard HTML templates.

-

Write html/template and Go handlers, and the page updates itself. The goal is app-like screens without an SPA, a JSON API or a build step, so there's no JavaScript here that you have to write.

+

Write html/template and Go handlers. The server re-renders on each action and patches only what changed. Every demo below is a real app running on this page, and none of it is JavaScript you had to write.

Get started → Read the docs @@ -54,8 +53,8 @@ layout: landing
The app
-

A shared greeting wall, running on this page.

-

Type a name. Your headline updates and your line joins the wall, along with everyone else's — including anyone else reading this page right now. Open this page in a second tab and watch it land there too, with no reload. The code for all of it is directly below.

+

Everyone reading this page writes to the same wall.

+

Type a name. Your headline updates and your line joins the wall underneath it, next to whoever else is here right now. Open this page in a second tab and watch the same line land there, with no reload. The whole program is directly below.

greet-wall · live, shared with every visitor
@@ -137,9 +136,9 @@ func main() {
-
What's going on
-

The parts of that worth a second look.

-

Every section below points at lines you have just read — except the pending state, which the wall has no slow work to demonstrate, and which says so. Each one also runs here as its own app, so you can try it.

+
Line by line
+

Four of those lines are worth a second look. One thing is missing.

+

Each section below picks out a line you have just read and runs it here as its own app, so you can try it on its own. The exception is the pending state: the wall answers instantly, so it has no slow work to show, and that section borrows two other apps instead.

@@ -229,7 +228,7 @@ func main() {
Pending state

Slow work has a pending state you can render.

-

This is the one thing the app above cannot show you: the wall answers instantly, so it has no pending state to render. Both apps below do have slow work. The first is the way to reach for — the pending flag is a template variable, so the spinner is ordinary Go and ordinary HTML, with no new attribute to learn.

+

These are the two borrowed apps, and both of them do slow work. Reach for the first one: the pending flag is a template variable, so the spinner is ordinary Go and ordinary HTML, with no new attribute to learn.

@@ -318,26 +317,26 @@ func main() {
How it compares
-

How this sits next to htmx, templ and LiveView.

-

Here a plain <button name="greet"> is already the action. lvt-* attributes are an escape hatch for what HTML cannot express, not the main interface.

+

This sits between htmx and LiveView.

+

Server-rendered HTML over a socket, like LiveView. Ordinary form markup and no state in the browser, like htmx. Both of them are years more mature than this, and each row below says where the other one still wins.

htmx
-
A similar HTML-first feel, with server-owned state and diffing built in, so there is less request wiring in the markup.
+
Works against any backend in any language, which this does not. Pick it up when the server is already written. Here the server keeps the state and computes the diff, so the markup carries no request wiring.
templ + htmx
-
Use Go's built-in html/template and keep live behavior in one app model instead of stacking layers.
+
templ type-checks your markup at compile time. html/template cannot, and that is a real thing to give up. The trade is a code generation step and one more library to track.
Alpine.js
-
Richer behavior without keeping a second copy of state in the browser.
+
Keeps real state in the browser — expressions, loops, computed values — which nothing here does. An lvt-el: attribute toggles a class or an attribute on a DOM event and stops there. That covers a dropdown. It does not cover a widget with its own model.
Phoenix LiveView
-
The same server-driven idea, in Go, and it still falls back to plain HTTP forms.
+
The same idea, and years ahead of it: production use at scale, and an ecosystem this does not have. This is that idea in Go, on a library still in alpha.
React SPA
-
Forms, CRUD, dashboards and shared views without splitting the product into an API and a frontend.
+
For a canvas editor or an offline-first app, a client framework is the right call. For a settings screen it means keeping two copies of the same data in sync, and that cost is the one thing this avoids.
Everything else
-

Other things in here.

-

It targets what Go teams actually ship: admin screens, internal tools, CRUD, dashboards, approvals, uploads, auth, and the occasional shared view.

+

The rest of the docs covers the ordinary screens.

+

Admin panels, internal tools, CRUD, dashboards, approvals, uploads, auth, and the occasional shared view. That is what this is for.

-

The UI patterns catalog has focused examples: loading states, inline validation, SPA-style navigation, sortable tables, pubsub, presence, server push. This site runs on LiveTemplate itself. See how it works.

+

The UI patterns catalog has focused examples: loading states, inline validation, SPA-style navigation, sortable tables, pubsub, presence, server push. A LiveTemplate app renders every page on this site, including this one. See how it works.

-

Getting started.

+

Install it, then pick a recipe.

$ go get github.com/livetemplate/livetemplate