ikurotime / gitgud

public
main / internal/interface/web/templates/layout.templ
7.6 KB · Templ Raw
  1package templates
  2
  3import "gitgud/internal/domain"
  4
  5templ Layout(title string, user *domain.User) {
  6	<!DOCTYPE html>
  7	<html lang="en" class="dark">
  8		<head>
  9			<meta charset="UTF-8"/>
 10			<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
 11			<title>{ title }</title>
 12			<link rel="preconnect" href="https://fonts.googleapis.com"/>
 13			<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin/>
 14			<link href="https://fonts.googleapis.com/css2?family=Geist:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500;600;700&display=swap" rel="stylesheet"/>
 15			<script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script>
 16			<style type="text/tailwindcss">
 17				@theme {
 18					--color-canvas: #0a0a0a;
 19					--color-panel: #0e0e0e;
 20					--color-panel2: #161616;
 21					--color-line: #1f1f1f;
 22					--color-line2: #2a2a2a;
 23					--color-ink: #ededed;
 24					--color-muted: #a1a1a1;
 25					--color-faint: #6e6e6e;
 26					--color-accent: #ededed;
 27					--color-accenthi: #ffffff;
 28					--color-grass: #ededed;
 29					--color-grasshi: #ffffff;
 30					--color-grassink: #0a0a0a;
 31					--color-open: #3fb950;
 32					--color-closed: #f85149;
 33					--color-merged: #a371f7;
 34					--color-warn: #d29922;
 35					--font-sans: "Geist", ui-sans-serif, system-ui, sans-serif;
 36					--font-mono: "JetBrains Mono", ui-monospace, monospace;
 37				}
 38				@layer base {
 39					html { color-scheme: dark; }
 40					body { @apply bg-canvas text-ink font-sans antialiased; }
 41					body::before {
 42						content: "";
 43						position: fixed;
 44						inset: 0;
 45						pointer-events: none;
 46						z-index: 0;
 47						background: radial-gradient(680px 320px at 50% -120px, rgba(255,255,255,.05), transparent 70%);
 48					}
 49					a { @apply transition-colors; }
 50					code, pre, .mono { @apply font-mono; }
 51					::selection { background: rgba(255,255,255,.18); }
 52					::-webkit-scrollbar { width: 10px; height: 10px; }
 53					::-webkit-scrollbar-thumb { background: #2a2a2a; border-radius: 9999px; border: 2px solid #0a0a0a; }
 54					:focus-visible { outline: none; box-shadow: 0 0 0 2px rgba(255,255,255,.45); border-radius: 6px; }
 55					main { animation: fade .15s ease-out both; }
 56					@keyframes fade { from { opacity: 0; } to { opacity: 1; } }
 57					@media (prefers-reduced-motion: reduce) { main { animation: none; } }
 58				}
 59				@layer components {
 60					.btn { @apply inline-flex items-center gap-1.5 rounded-md border border-line2 bg-panel2 px-3 py-1.5 text-sm font-medium text-ink transition hover:border-faint hover:bg-line; }
 61					.btn-primary { @apply border-transparent bg-grass font-semibold text-grassink hover:bg-grasshi hover:border-transparent; }
 62					.btn-danger { @apply text-closed hover:border-closed hover:bg-closed/10; }
 63					.input, .textarea, .select { @apply w-full rounded-md border border-line2 bg-[#111111] px-3 py-2 text-sm text-ink placeholder:text-faint transition focus:border-faint; }
 64					.card { @apply rounded-md border border-line bg-panel; }
 65					.link { @apply text-ink hover:text-accent; }
 66					.muted { @apply text-muted; }
 67					.meta { @apply font-mono text-xs text-faint; }
 68					.tab { @apply inline-flex items-center gap-2 border-b-2 border-transparent pb-2.5 text-sm text-muted transition hover:text-ink; }
 69					.tab-active { @apply border-accent font-semibold text-ink; }
 70					.badge { @apply inline-flex items-center gap-1 rounded border border-line2 px-2 py-0.5 text-xs font-medium text-muted; }
 71					.badge-open { @apply gap-1.5 rounded-full border-transparent bg-open/16 px-3 py-1 font-medium text-open; }
 72					.badge-closed { @apply gap-1.5 rounded-full border-transparent bg-closed/16 px-3 py-1 font-medium text-[#ff8f86]; }
 73					.badge-merged { @apply gap-1.5 rounded-full border-transparent bg-merged/16 px-3 py-1 font-medium text-[#c8a6f7]; }
 74					.label-bug { @apply inline-flex items-center rounded-full bg-closed/16 px-2.5 py-0.5 text-[10px] font-medium text-[#ff8f86]; }
 75					.label-enh { @apply inline-flex items-center rounded-full bg-accent/16 px-2.5 py-0.5 text-[10px] font-medium text-accenthi; }
 76					.label-good { @apply inline-flex items-center rounded-full bg-warn/16 px-2.5 py-0.5 text-[10px] font-medium text-warn; }
 77					.row { @apply transition hover:bg-panel2; }
 78					.diff-add { @apply bg-open/12 text-[#7ee2a0]; }
 79					.diff-del { @apply bg-closed/12 text-[#f2b8b3]; }
 80					.diff-hunk { @apply bg-panel2 text-faint; }
 81					.prose-dark { @apply text-sm leading-relaxed text-muted; }
 82					.prose-dark h1 { @apply mt-5 mb-3 text-xl font-semibold text-ink first:mt-0; }
 83					.prose-dark h2 { @apply mt-5 mb-2 text-lg font-semibold text-ink first:mt-0; }
 84					.prose-dark h3 { @apply mt-4 mb-2 font-semibold text-ink; }
 85					.prose-dark p { @apply my-3; }
 86					.prose-dark a { @apply text-accent hover:underline; }
 87					.prose-dark strong { @apply font-semibold text-ink; }
 88					.prose-dark ul { @apply my-3 list-disc pl-5; }
 89					.prose-dark ol { @apply my-3 list-decimal pl-5; }
 90					.prose-dark li { @apply my-1; }
 91					.prose-dark code { @apply rounded border border-line2 bg-panel2 px-1.5 py-0.5 text-[.85em] text-accent; }
 92					.prose-dark pre { @apply my-3 overflow-x-auto rounded-lg border border-line bg-panel2 p-4 text-xs; }
 93					.prose-dark pre code { @apply bg-transparent p-0; }
 94					.prose-dark blockquote { @apply my-3 border-l-2 border-line2 pl-4 text-faint; }
 95					.prose-dark hr { @apply my-5 border-line; }
 96				}
 97			</style>
 98		</head>
 99		<body>
100			<header class="sticky top-0 z-20 border-b border-line bg-canvas/80 backdrop-blur-xl">
101				<nav class="mx-auto flex h-14 max-w-5xl items-center justify-between px-6">
102					<a href="/" class="flex items-center gap-2.5">
103						<span class="text-accent">@gitLogo("size-5")</span>
104						<span class="font-mono font-semibold tracking-tight text-ink">gitgud</span>
105					</a>
106					<div class="flex items-center gap-4 text-sm">
107						if user != nil {
108							<a class="btn" href="/new">+ New</a>
109							<div class="flex items-center gap-2">
110								@avatar(user.Username, "size-6")
111								<a class="link mono text-muted" href={ templ.SafeURL("/" + user.Username) }>{ user.Username }</a>
112							</div>
113							<form method="post" action="/logout">
114								@csrfField()
115								<button class="text-muted transition hover:text-ink" type="submit">Sign out</button>
116							</form>
117						} else {
118							<a class="text-muted transition hover:text-ink" href="/login">Sign in</a>
119							<a class="btn btn-primary" href="/register">Register</a>
120						}
121					</div>
122				</nav>
123			</header>
124			if flashOf(ctx) != "" {
125				<div class="mx-auto max-w-5xl px-6 pt-4">
126					<div class="card flex items-center gap-2 border-accent/30 bg-accent/10 px-4 py-2.5 text-sm text-ink">
127						<span class="size-1.5 rounded-full bg-accent"></span>
128						{ flashOf(ctx) }
129					</div>
130				</div>
131			}
132			<main class="relative z-10 mx-auto max-w-5xl px-6 py-8">
133				{ children... }
134			</main>
135		</body>
136	</html>
137}
138
139templ csrfField() {
140	<input type="hidden" name="csrf_token" value={ csrfToken(ctx) }/>
141}
142
143// gitLogo renders the gitgud branch-network mark. Color is inherited via currentColor.
144templ gitLogo(class string) {
145	<svg class={ class } viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
146		<circle cx="6" cy="5.5" r="2.5"></circle>
147		<circle cx="6" cy="18.5" r="2.5"></circle>
148		<circle cx="18" cy="12" r="2.5"></circle>
149		<path d="M6 8v8"></path>
150		<path d="M6 12h6.5a3 3 0 0 0 3-3V9.5"></path>
151	</svg>
152}
153
154// avatar renders a deterministic gradient avatar with the user's initial.
155templ avatar(name, class string) {
156	<span
157		class={ "inline-flex shrink-0 items-center justify-center rounded-full bg-gradient-to-br from-[#2a2a2a] to-[#0e0e0e] font-semibold text-ink ring-1 ring-line2", class }
158		style="font-size:.62em"
159	>{ initial(name) }</span>
160}