web.md

Web apps

Default to a light server-rendered stack unless the product is clearly frontend-heavy.

Default light web stack

  • net/http
  • html/template or text/template as appropriate
  • embed.FS for templates and static assets
  • plain CSS with semantic variables
  • semantic HTML forms and links before client-side JavaScript
  • small Node toolchain only for things like Prettier/template formatting when needed

This is the default for admin panels, dashboards, release trackers, internal tools, and server-rendered apps with modest interaction.

Light web layout

cmd/<app>/main.go
internal/config/...
internal/db/...
internal/<domain>/...
internal/web/ or web/
├── handler.go
└── static/
    ├── head.html.tmpl
    ├── page.html.tmpl
    ├── styles.css
    └── reset.css

Use template.ParseFS with named shared templates. Keep handlers thin: parse input, call domain code, render or redirect.

CSS and accessibility

  • Use semantic HTML and real form controls.
  • Include viewport and colour-scheme metadata.
  • Use CSS custom properties as semantic design tokens. OKLCH is preferred for colour palettes when browser support is acceptable.
  • Support dark mode by overriding semantic tokens under prefers-color-scheme.
  • Use spacing, radius, type, and measure scales instead of one-off values.
  • Prefer simple responsive layouts with CSS grid/flex.
  • Use accessible fonts when bundling fonts; include licence metadata when the project is REUSE-compliant.
  • Do not use ARIA to compensate for avoidable non-semantic markup.

Security

  • Treat form/query values as untrusted.
  • Use bluemonday when rendering user-provided HTML or Markdown-derived HTML.
  • Keep auth/session logic in a domain package; handlers should not duplicate password or token handling.
  • Prefer database/sql with explicit migrations for small SQLite-backed apps.

Escalate to frontend-heavy only when needed

Escalate to a frontend-heavy stack only when the UI needs rich local state, complex component composition, desktop integration, drag/drop, long-running event streams, or substantial client-side interaction. For Wails, prefer Svelte 5 + TypeScript + Vite; Tailwind and shadcn-svelte remain explicit project choices. If the page can remain a form and a template, keep it light.