tenseleyflow/shithub / af6a08c

Browse files

Add nav + footer partials, error pages, and Primer-aligned chrome styles

Authored by mfwolffe <wolffemf@dukes.jmu.edu>
SHA
af6a08c2ae21e953665a87f2b244a98620bea9d0
Parents
f579c60
Tree
96bfc74

9 changed files

StatusFile+-
M internal/web/static/css/shithub.css 179 26
A internal/web/templates/_footer.html 17 0
M internal/web/templates/_layout.html 9 5
A internal/web/templates/_nav.html 16 0
A internal/web/templates/errors/403.html 21 0
A internal/web/templates/errors/404.html 17 0
A internal/web/templates/errors/429.html 16 0
A internal/web/templates/errors/500.html 18 0
M internal/web/templates/hello.html 6 6
internal/web/static/css/shithub.cssmodified
@@ -2,20 +2,26 @@
22
 /*
33
  * shithub — base styles.
44
  *
5
- * S00 ships a tiny stylesheet sufficient for the hello page across light /
6
- * dark / auto / high-contrast themes. S02 introduces the full theme system
7
- * via Primer primitives; this file becomes one layer in a stack.
5
+ * S02 layers our own chrome over the Primer CSS that ships in
6
+ * /static/primer/primer.css. We pull a small set of token overrides so the
7
+ * theme system (light / dark / auto / high-contrast) drives everything via
8
+ * CSS custom properties.
89
  *
9
- * Color tokens follow Primer's naming so the migration in S02 is mechanical.
10
+ * Color tokens follow Primer's naming so most of these become migration
11
+ * candidates as Primer's primitives stabilize.
1012
  */
1113
 
1214
 :root {
1315
   --canvas-default: #ffffff;
1416
   --canvas-subtle: #f6f8fa;
17
+  --canvas-inset: #eaeef2;
1518
   --fg-default: #1f2328;
1619
   --fg-muted: #59636e;
1720
   --border-default: #d0d7de;
21
+  --border-muted: #d8dee4;
1822
   --accent-fg: #0969da;
23
+  --accent-emphasis: #0969da;
24
+  --success-fg: #1a7f37;
1925
   --danger-fg: #cf222e;
2026
   --shithub-mark: var(--danger-fg);
2127
 }
@@ -23,26 +29,32 @@
2329
 [data-theme="dark"] {
2430
   --canvas-default: #0d1117;
2531
   --canvas-subtle: #161b22;
32
+  --canvas-inset: #010409;
2633
   --fg-default: #f0f6fc;
2734
   --fg-muted: #9198a1;
2835
   --border-default: #3d444d;
36
+  --border-muted: #232a33;
2937
   --accent-fg: #4493f8;
38
+  --accent-emphasis: #1f6feb;
39
+  --success-fg: #3fb950;
3040
   --danger-fg: #f85149;
3141
 }
3242
 
3343
 [data-theme="high-contrast"] {
3444
   --canvas-default: #000000;
3545
   --canvas-subtle: #0a0c10;
46
+  --canvas-inset: #000000;
3647
   --fg-default: #ffffff;
3748
   --fg-muted: #d9dee3;
3849
   --border-default: #7a828e;
50
+  --border-muted: #525964;
3951
   --accent-fg: #71b7ff;
52
+  --accent-emphasis: #409eff;
53
+  --success-fg: #4ed162;
4054
   --danger-fg: #ff6a69;
4155
 }
4256
 
43
-* {
44
-  box-sizing: border-box;
45
-}
57
+* { box-sizing: border-box; }
4658
 
4759
 html, body {
4860
   margin: 0;
@@ -60,37 +72,136 @@ a {
6072
 }
6173
 a:hover { text-decoration: underline; }
6274
 
75
+code {
76
+  font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;
77
+  font-size: 0.875em;
78
+  background: var(--canvas-subtle);
79
+  padding: 0.1em 0.35em;
80
+  border-radius: 4px;
81
+}
82
+
83
+/* ========== Nav ========== */
84
+
85
+.shithub-body {
86
+  min-height: 100vh;
87
+  display: flex;
88
+  flex-direction: column;
89
+}
90
+
91
+.shithub-nav {
92
+  display: flex;
93
+  align-items: center;
94
+  gap: 1.5rem;
95
+  padding: 0.75rem 1.25rem;
96
+  background: var(--canvas-subtle);
97
+  border-bottom: 1px solid var(--border-default);
98
+}
99
+
100
+.shithub-nav-brand {
101
+  display: flex;
102
+  align-items: center;
103
+  gap: 0.5rem;
104
+  color: var(--fg-default);
105
+  font-weight: 600;
106
+  letter-spacing: -0.01em;
107
+}
108
+.shithub-nav-brand:hover { text-decoration: none; }
109
+.shithub-nav-brand svg { color: var(--shithub-mark); }
110
+
111
+.shithub-nav-links {
112
+  display: flex;
113
+  gap: 1rem;
114
+  flex: 1;
115
+}
116
+.shithub-nav-links a {
117
+  color: var(--fg-default);
118
+  font-size: 0.9rem;
119
+}
120
+
121
+.shithub-nav-actions {
122
+  display: flex;
123
+  gap: 0.5rem;
124
+}
125
+
126
+.shithub-button {
127
+  display: inline-flex;
128
+  align-items: center;
129
+  padding: 0.4rem 0.85rem;
130
+  border-radius: 6px;
131
+  font-size: 0.875rem;
132
+  font-weight: 500;
133
+  border: 1px solid transparent;
134
+  cursor: pointer;
135
+}
136
+.shithub-button-ghost {
137
+  color: var(--fg-default);
138
+  border-color: var(--border-default);
139
+  background: transparent;
140
+}
141
+.shithub-button-primary {
142
+  color: #fff;
143
+  background: var(--accent-emphasis);
144
+  border-color: var(--accent-emphasis);
145
+}
146
+.shithub-button-primary:hover { text-decoration: none; opacity: 0.92; }
147
+
148
+/* ========== Main + Footer ========== */
149
+
150
+.shithub-main {
151
+  flex: 1;
152
+  width: 100%;
153
+}
154
+
155
+.shithub-footer {
156
+  border-top: 1px solid var(--border-default);
157
+  background: var(--canvas-subtle);
158
+  padding: 1.25rem;
159
+  font-size: 0.85rem;
160
+  color: var(--fg-muted);
161
+}
162
+.shithub-footer-inner {
163
+  max-width: 1200px;
164
+  margin: 0 auto;
165
+  display: flex;
166
+  justify-content: space-between;
167
+  align-items: center;
168
+  gap: 1rem;
169
+}
170
+.shithub-footer-brand {
171
+  display: flex;
172
+  align-items: center;
173
+  gap: 0.4rem;
174
+  color: var(--fg-default);
175
+}
176
+.shithub-footer-brand svg { color: var(--shithub-mark); }
177
+.shithub-footer-meta { color: var(--fg-muted); margin-left: 0.5rem; font-size: 0.8rem; }
178
+.shithub-footer-links { display: flex; gap: 1rem; }
179
+
180
+/* ========== Hello page ========== */
181
+
63182
 .hello {
64183
   max-width: 640px;
65184
   margin: 4rem auto;
66185
   padding: 2rem 1.5rem;
67186
   text-align: center;
68187
 }
69
-
70188
 .hello-logo {
71189
   margin: 0 auto 1.5rem;
72190
   width: 160px;
73191
   height: 160px;
74192
   color: var(--shithub-mark);
75193
 }
76
-
77
-.hello-logo svg {
78
-  width: 100%;
79
-  height: 100%;
80
-}
81
-
194
+.hello-logo svg { width: 100%; height: 100%; }
82195
 .hello-title {
83196
   font-size: 2.75rem;
84197
   margin: 0 0 0.5rem;
85198
   letter-spacing: -0.02em;
86199
 }
87
-
88200
 .hello-tagline {
89201
   color: var(--fg-muted);
90202
   font-size: 1.15rem;
91203
   margin: 0 0 2rem;
92204
 }
93
-
94205
 .hello-meta {
95206
   display: grid;
96207
   grid-template-columns: max-content 1fr;
@@ -105,21 +216,13 @@ a:hover { text-decoration: underline; }
105216
   font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;
106217
   font-size: 0.875rem;
107218
 }
108
-
109
-.hello-meta dt {
110
-  color: var(--fg-muted);
111
-}
112
-
113
-.hello-meta dd {
114
-  margin: 0;
115
-}
116
-
219
+.hello-meta dt { color: var(--fg-muted); }
220
+.hello-meta dd { margin: 0; }
117221
 .hello-status {
118222
   color: var(--fg-muted);
119223
   margin: 0 auto 2rem;
120224
   max-width: 36rem;
121225
 }
122
-
123226
 .hello-links {
124227
   display: flex;
125228
   justify-content: center;
@@ -127,3 +230,53 @@ a:hover { text-decoration: underline; }
127230
   font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;
128231
   font-size: 0.875rem;
129232
 }
233
+
234
+/* ========== Error pages ========== */
235
+
236
+.error-page {
237
+  max-width: 540px;
238
+  margin: 6rem auto;
239
+  padding: 2rem 1.5rem;
240
+  text-align: center;
241
+}
242
+.error-glyph {
243
+  display: inline-flex;
244
+  width: 64px; height: 64px;
245
+  align-items: center; justify-content: center;
246
+  border-radius: 50%;
247
+  background: var(--canvas-subtle);
248
+  color: var(--danger-fg);
249
+  margin-bottom: 1.5rem;
250
+}
251
+.error-glyph svg { width: 32px; height: 32px; }
252
+.error-status {
253
+  margin: 0;
254
+  font-size: 4rem;
255
+  letter-spacing: -0.04em;
256
+  color: var(--fg-default);
257
+}
258
+.error-title {
259
+  margin: 0 0 0.75rem;
260
+  font-size: 1.4rem;
261
+  font-weight: 500;
262
+}
263
+.error-detail {
264
+  color: var(--fg-muted);
265
+  margin: 0 0 2rem;
266
+  max-width: 36rem;
267
+  margin-left: auto;
268
+  margin-right: auto;
269
+}
270
+.error-hint a {
271
+  display: inline-block;
272
+  padding: 0.5rem 1rem;
273
+  border: 1px solid var(--border-default);
274
+  border-radius: 6px;
275
+  color: var(--fg-default);
276
+}
277
+.error-hint a:hover { text-decoration: none; background: var(--canvas-subtle); }
278
+.error-request-id {
279
+  margin-top: 2rem;
280
+  color: var(--fg-muted);
281
+  font-size: 0.8rem;
282
+}
internal/web/templates/_layout.htmlmodified
@@ -3,9 +3,9 @@
33
 <html lang="en" data-theme="auto">
44
 <head>
55
   <script>
6
-    // Theme flash avoidance: read cookie or system preference and apply
7
-    // before any CSS computes. Establishes the dark/light/auto/high-contrast
8
-    // contract S02 will broaden.
6
+    // Theme flash avoidance: read the cookie or system preference and apply
7
+    // before any CSS computes. The four themes are: light, dark, auto,
8
+    // high-contrast (S10 wires the picker; S02 just enforces the contract).
99
     (function () {
1010
       var match = document.cookie.match(/(?:^|; )theme=([^;]+)/);
1111
       var theme = match ? decodeURIComponent(match[1]) : "auto";
@@ -21,11 +21,15 @@
2121
   <meta name="description" content="shithub — GitHub. Open source. Without Copilot.">
2222
   <title>{{ .Title }} · shithub</title>
2323
   <link rel="icon" type="image/svg+xml" href="/static/logo/favicon.svg">
24
-  <link rel="stylesheet" href="/static/css/shithub.css">
2524
   <link rel="stylesheet" href="/static/primer/primer.css" onerror="this.remove()">
25
+  <link rel="stylesheet" href="/static/css/shithub.css">
2626
 </head>
27
-<body>
27
+<body class="shithub-body">
28
+{{ template "nav" . }}
29
+<main class="shithub-main">
2830
 {{ template "page" . }}
31
+</main>
32
+{{ template "footer" . }}
2933
 </body>
3034
 </html>
3135
 {{- end }}
internal/web/templates/_nav.htmladded
@@ -0,0 +1,16 @@
1
+{{ define "nav" -}}
2
+<header class="shithub-nav" role="banner">
3
+  <a href="/" class="shithub-nav-brand" aria-label="shithub home">
4
+    {{ octicon "shithub" }}
5
+    <span>shithub</span>
6
+  </a>
7
+  <nav class="shithub-nav-links" aria-label="Primary">
8
+    <a href="/explore">Explore</a>
9
+    <a href="/about">About</a>
10
+  </nav>
11
+  <div class="shithub-nav-actions">
12
+    <a href="/login" class="shithub-button shithub-button-ghost">Sign in</a>
13
+    <a href="/signup" class="shithub-button shithub-button-primary">Sign up</a>
14
+  </div>
15
+</header>
16
+{{- end }}
internal/web/templates/errors/403.htmladded
@@ -0,0 +1,21 @@
1
+{{ define "page" -}}
2
+<section class="error-page">
3
+  <div class="error-glyph" aria-hidden="true">{{ octicon "alert" }}</div>
4
+  <h1 class="error-status">403</h1>
5
+  <p class="error-title">Access denied.</p>
6
+  <p class="error-detail">
7
+    {{ if eq .Message "csrf" -}}
8
+    Your session is stale or this form was submitted from a different
9
+    origin. Reload the page and try again.
10
+    {{- else -}}
11
+    You don't have permission to perform this action.
12
+    {{- end }}
13
+  </p>
14
+  <p class="error-hint">
15
+    <a href="/">Back to the dashboard</a>
16
+  </p>
17
+  {{ if .RequestID -}}
18
+  <p class="error-request-id">request_id: <code>{{ .RequestID }}</code></p>
19
+  {{- end }}
20
+</section>
21
+{{- end }}
internal/web/templates/errors/404.htmladded
@@ -0,0 +1,17 @@
1
+{{ define "page" -}}
2
+<section class="error-page">
3
+  <div class="error-glyph" aria-hidden="true">{{ octicon "alert" }}</div>
4
+  <h1 class="error-status">404</h1>
5
+  <p class="error-title">This isn't the repo you're looking for.</p>
6
+  <p class="error-detail">
7
+    The page <code>{{ .Message }}</code> doesn't exist, was moved, or you don't
8
+    have permission to view it.
9
+  </p>
10
+  <p class="error-hint">
11
+    <a href="/">Back to the dashboard</a>
12
+  </p>
13
+  {{ if .RequestID -}}
14
+  <p class="error-request-id">request_id: <code>{{ .RequestID }}</code></p>
15
+  {{- end }}
16
+</section>
17
+{{- end }}
internal/web/templates/errors/429.htmladded
@@ -0,0 +1,16 @@
1
+{{ define "page" -}}
2
+<section class="error-page">
3
+  <div class="error-glyph" aria-hidden="true">{{ octicon "alert" }}</div>
4
+  <h1 class="error-status">429</h1>
5
+  <p class="error-title">Too many requests.</p>
6
+  <p class="error-detail">
7
+    Slow down — you've exceeded the rate limit. Try again in a few moments.
8
+  </p>
9
+  <p class="error-hint">
10
+    <a href="/">Back to the dashboard</a>
11
+  </p>
12
+  {{ if .RequestID -}}
13
+  <p class="error-request-id">request_id: <code>{{ .RequestID }}</code></p>
14
+  {{- end }}
15
+</section>
16
+{{- end }}
internal/web/templates/errors/500.htmladded
@@ -0,0 +1,18 @@
1
+{{ define "page" -}}
2
+<section class="error-page">
3
+  <div class="error-glyph" aria-hidden="true">{{ octicon "alert" }}</div>
4
+  <h1 class="error-status">500</h1>
5
+  <p class="error-title">Something went sideways on our end.</p>
6
+  <p class="error-detail">
7
+    The server hit an unexpected error while handling your request. The
8
+    incident has been logged.
9
+  </p>
10
+  <p class="error-hint">
11
+    <a href="/">Back to the dashboard</a>
12
+  </p>
13
+  {{ if .RequestID -}}
14
+  <p class="error-request-id">If you contact support, please quote
15
+    request_id: <code>{{ .RequestID }}</code></p>
16
+  {{- end }}
17
+</section>
18
+{{- end }}
internal/web/templates/hello.htmlmodified
@@ -1,5 +1,5 @@
11
 {{ define "page" -}}
2
-<main class="hello">
2
+<section class="hello">
33
   <div class="hello-logo" aria-hidden="true">
44
     {{ .LogoSVG }}
55
   </div>
@@ -13,14 +13,14 @@
1313
   </dl>
1414
 
1515
   <p class="hello-status">
16
-    Pre-launch. Sprint 00 — project scaffolding — is shipping. Every later
17
-    sprint adds real product surface; see the planning index for the full
18
-    roadmap.
16
+    Pre-launch. Sprint 02 — the web shell — is shipping. The chi router,
17
+    middleware stack, error pages, and Primer-themed chrome are now in.
1918
   </p>
2019
 
21
-  <nav class="hello-links" aria-label="Project links">
20
+  <nav class="hello-links" aria-label="Health endpoints">
2221
     <a href="/healthz">/healthz</a>
2322
     <a href="/readyz">/readyz</a>
23
+    <a href="/internal/panic">/internal/panic</a>
2424
   </nav>
25
-</main>
25
+</section>
2626
 {{- end }}