feat: bootstrap design kit and vaporwave demo baseline

This commit is contained in:
2026-02-24 01:13:58 +03:00
commit d0cffab6a1
95 changed files with 11949 additions and 0 deletions

View File

@@ -0,0 +1,12 @@
run:
go run ./cmd/demo-server
build:
go build ./cmd/demo-server
test:
go test ./...
fmt:
gofmt -w $$(find . -name '*.go' -type f)

View File

@@ -0,0 +1,10 @@
# Go net/http Web Skeleton
Minimal starter skeleton for a Go web app using:
- `net/http`
- `html/template`
- embedded templates and static assets
This scaffold is intended as a starting point that host repositories can adapt.

View File

@@ -0,0 +1,23 @@
package web
import (
"net/http"
)
type IndexViewData struct {
Title string
}
func (s *Server) handleIndex(w http.ResponseWriter, r *http.Request) {
if r.URL.Path != "/" {
http.NotFound(w, r)
return
}
_ = s.render(w, "base.html", IndexViewData{Title: "Home"})
}
func (s *Server) handleHealthz(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
_, _ = w.Write([]byte("ok"))
}

View File

@@ -0,0 +1,37 @@
package web
import (
"html/template"
"io/fs"
"net/http"
appweb "{{ .module_path }}/web"
)
type Server struct {
mux *http.ServeMux
tmpl *template.Template
}
func NewServer() (*Server, error) {
tmpl, err := parseTemplates()
if err != nil {
return nil, err
}
s := &Server{
mux: http.NewServeMux(),
tmpl: tmpl,
}
s.registerRoutes()
return s, nil
}
func (s *Server) Handler() http.Handler { return s.mux }
func (s *Server) registerRoutes() {
s.mux.HandleFunc("/", s.handleIndex)
s.mux.HandleFunc("/healthz", s.handleHealthz)
staticFS, _ := fs.Sub(appweb.Assets, "static")
s.mux.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.FS(staticFS))))
}

View File

@@ -0,0 +1,27 @@
package web
import (
"fmt"
"html/template"
"net/http"
appweb "{{ .module_path }}/web"
)
func parseTemplates() (*template.Template, error) {
t, err := template.ParseFS(appweb.Assets, "templates/*.html")
if err != nil {
return nil, fmt.Errorf("parse templates: %w", err)
}
return t, nil
}
func (s *Server) render(w http.ResponseWriter, name string, data any) error {
w.Header().Set("Content-Type", "text/html; charset=utf-8")
if err := s.tmpl.ExecuteTemplate(w, name, data); err != nil {
http.Error(w, "template error", http.StatusInternalServerError)
return fmt.Errorf("render %s: %w", name, err)
}
return nil
}

View File

@@ -0,0 +1,9 @@
package web
import "embed"
// Assets contains templates and static files.
//
//go:embed templates/* static/*
var Assets embed.FS

View File

@@ -0,0 +1,46 @@
:root {
--bg: #e6e8ee;
--ink: #1f2023;
--muted: #5d646f;
--card: linear-gradient(180deg, rgba(252,252,253,0.99), rgba(240, 244, 249, 0.97) 44%, rgba(228, 238, 248, 0.96) 78%, rgba(221, 234, 248, 0.96));
--line: #b9cbe0;
--accent: linear-gradient(180deg, #85b8ef 0%, #93c6fb 26%, #abd8ff 55%, #c7e8ff 82%, #def2ff 100%);
--accent-line: #88a4d4;
}
* { box-sizing: border-box; }
body {
margin: 0;
font-family: "Lucida Grande", "Helvetica Neue", Helvetica, Arial, sans-serif;
background:
radial-gradient(760px 360px at 14% 6%, rgba(255,255,255,0.82), transparent 72%),
radial-gradient(760px 360px at 88% 8%, rgba(154, 203, 247, 0.16), transparent 74%),
repeating-linear-gradient(to bottom, rgba(255,255,255,0.05) 0 1px, rgba(170,182,198,0.03) 1px 3px),
linear-gradient(#eef1f6, #dde2ea);
color: var(--ink);
}
.shell {
max-width: 880px;
margin: 0 auto;
padding: 24px 16px 48px;
}
.hero { margin-bottom: 16px; }
.eyebrow {
margin: 0;
color: #5f6772;
text-transform: uppercase;
letter-spacing: 0.08em;
font-size: 12px;
}
h1 { margin: 6px 0 0; font-size: 32px; }
.card {
background:
linear-gradient(180deg, rgba(255,255,255,0.70), rgba(255,255,255,0.18) 28%, rgba(255,255,255,0.0) 29%),
repeating-linear-gradient(to bottom, rgba(255,255,255,0.26) 0 1px, rgba(188,205,223,0.10) 1px 3px),
var(--card);
border: 1px solid var(--line);
border-radius: 14px;
padding: 16px;
box-shadow: 0 10px 20px rgba(78, 107, 139, 0.09), inset 0 1px 0 rgba(255,255,255,0.96);
}
code { background: #eef3fb; padding: 1px 5px; border-radius: 6px; }

View File

@@ -0,0 +1,2 @@
document.documentElement.classList.add("js");

View File

@@ -0,0 +1,22 @@
{{ define "base.html" }}
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>{{ .Title }}</title>
<link rel="stylesheet" href="/static/css/app.css">
</head>
<body>
<main class="shell">
<header class="hero">
<p class="eyebrow">Go Web Scaffold</p>
<h1>{{ .Title }}</h1>
</header>
{{ template "content" . }}
</main>
<script src="/static/js/app.js" defer></script>
</body>
</html>
{{ end }}

View File

@@ -0,0 +1,11 @@
{{ define "content" }}
<section class="card">
<p>Minimal net/http + html/template scaffold.</p>
<ul>
<li><code>GET /</code> page</li>
<li><code>GET /healthz</code> healthcheck</li>
<li><code>/static/*</code> embedded assets</li>
</ul>
</section>
{{ end }}