Skip to content

Commit 23028e5

Browse files
authored
add blog with markdown posts, goldmark rendering, and htmx (#33)
1 parent 9bf5c02 commit 23028e5

8 files changed

Lines changed: 830 additions & 4 deletions

File tree

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,4 @@ dist/
33
node_modules/
44
.wrangler/
55
.claude/
6+
threadlocked

blog.html

Lines changed: 315 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,315 @@
1+
<!doctype html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="utf-8" />
5+
<meta name="viewport" content="width=device-width, initial-scale=1" />
6+
<script>
7+
(function () {
8+
var saved;
9+
try { saved = localStorage.getItem("theme"); } catch (e) {}
10+
var prefersDark = window.matchMedia && window.matchMedia("(prefers-color-scheme: dark)").matches;
11+
document.documentElement.dataset.theme = saved || (prefersDark ? "dark" : "light");
12+
})();
13+
</script>
14+
<title>threadlocked · blog</title>
15+
<style>
16+
:root {
17+
--base: #faf4ed;
18+
--surface: #fffaf3;
19+
--overlay: #f2e9e1;
20+
--muted: #9893a5;
21+
--subtle: #797593;
22+
--text: #575279;
23+
--love: #b4637a;
24+
--gold: #ea9d34;
25+
--rose: #d7827e;
26+
--pine: #286983;
27+
--foam: #56949f;
28+
--iris: #907aa9;
29+
--highlight: #f4ede8;
30+
--radius: 0;
31+
--theme: var(--base);
32+
--tertiary: var(--muted);
33+
--secondary: var(--subtle);
34+
}
35+
:root[data-theme="dark"] {
36+
--base: #191724;
37+
--surface: #1f1d2e;
38+
--overlay: #26233a;
39+
--muted: #6e6a86;
40+
--subtle: #908caa;
41+
--text: #e0def4;
42+
--love: #eb6f92;
43+
--gold: #f6c177;
44+
--rose: #ebbcba;
45+
--pine: #31748f;
46+
--foam: #9ccfd8;
47+
--iris: #c4a7e7;
48+
--highlight: #21202e;
49+
}
50+
51+
* { box-sizing: border-box; }
52+
53+
::-webkit-scrollbar-track {
54+
background: 0 0;
55+
}
56+
57+
::-webkit-scrollbar-thumb {
58+
background: var(--tertiary);
59+
border: 5px solid var(--theme);
60+
border-radius: var(--radius);
61+
}
62+
63+
::-webkit-scrollbar-thumb:hover {
64+
background: var(--secondary);
65+
}
66+
67+
::-webkit-scrollbar:not(.highlighttable, .highlight table, .gist .highlight) {
68+
background: var(--theme);
69+
}
70+
71+
@media screen and (min-width: 768px) {
72+
::-webkit-scrollbar {
73+
width: 19px;
74+
height: 11px;
75+
}
76+
}
77+
78+
body {
79+
margin: 0;
80+
min-height: 100vh;
81+
background: var(--base);
82+
color: var(--text);
83+
font: 16px/1.6 ui-monospace, "SF Mono", "JetBrains Mono", Menlo, Consolas, monospace;
84+
-webkit-font-smoothing: antialiased;
85+
transition: background 0.25s ease, color 0.25s ease;
86+
}
87+
88+
main {
89+
max-width: 1180px;
90+
margin: 0 auto;
91+
padding: 4rem 1.5rem 6rem;
92+
}
93+
94+
header {
95+
display: flex;
96+
align-items: flex-start;
97+
justify-content: space-between;
98+
gap: 1rem;
99+
margin-bottom: 2rem;
100+
}
101+
102+
h1 {
103+
margin: 0 0 0.45rem;
104+
font-size: 1.9rem;
105+
font-weight: 600;
106+
letter-spacing: -0.02em;
107+
}
108+
h1 .ring { color: var(--iris); }
109+
110+
.parent-title { color: inherit; text-decoration: none; }
111+
.parent-title:hover { color: var(--iris); }
112+
.sep { color: var(--muted); margin: 0 0.2em; }
113+
.current-page { color: var(--foam); }
114+
115+
.desc { margin: 0; color: var(--subtle); font-size: 1rem; }
116+
117+
.actions { flex: none; display: flex; gap: 0.5rem; }
118+
119+
.icon-btn {
120+
appearance: none;
121+
display: grid;
122+
place-items: center;
123+
width: 2.4rem;
124+
height: 2.4rem;
125+
background: var(--surface);
126+
color: var(--subtle);
127+
border: 1px solid var(--overlay);
128+
border-radius: 0;
129+
padding: 0;
130+
cursor: pointer;
131+
transition: background 0.2s ease, border-color 0.2s ease, color 0.2s ease;
132+
}
133+
.icon-btn:hover { border-color: var(--iris); color: var(--iris); }
134+
.icon-btn svg { width: 1.15rem; height: 1.15rem; display: block; }
135+
136+
.post-list {
137+
display: flex;
138+
flex-direction: column;
139+
gap: 1rem;
140+
}
141+
142+
.post-card {
143+
border: 3px solid var(--overlay);
144+
border-radius: var(--radius);
145+
background: var(--surface);
146+
transition: border-color 0.18s ease, background 0.18s ease;
147+
}
148+
.post-card:hover {
149+
border-color: var(--foam);
150+
background: var(--highlight);
151+
}
152+
.post-card a {
153+
display: block;
154+
padding: 1.25rem 1.5rem;
155+
text-decoration: none;
156+
color: var(--text);
157+
}
158+
.post-card time {
159+
display: block;
160+
font-size: 0.85rem;
161+
color: var(--muted);
162+
margin-bottom: 0.25rem;
163+
}
164+
.post-card h2 {
165+
margin: 0;
166+
font-size: 1.15rem;
167+
font-weight: 500;
168+
}
169+
.post-card:hover h2 { color: var(--foam); }
170+
.post-card h2 .external-icon {
171+
vertical-align: middle;
172+
margin-left: 0.25em;
173+
color: var(--muted);
174+
transition: color 0.18s ease;
175+
}
176+
.post-card:hover h2 .external-icon { color: var(--foam); }
177+
178+
.empty-state {
179+
color: var(--muted);
180+
font-size: 0.9rem;
181+
padding: 2rem 0;
182+
}
183+
184+
.post { max-width: 72ch; }
185+
.post-title {
186+
margin: 0 0 0.25rem;
187+
font-size: 1.6rem;
188+
font-weight: 600;
189+
letter-spacing: -0.01em;
190+
}
191+
.post-date {
192+
display: block;
193+
color: var(--muted);
194+
font-size: 0.9rem;
195+
margin-bottom: 2rem;
196+
}
197+
.post-content { line-height: 1.75; }
198+
.post-content h1,
199+
.post-content h2,
200+
.post-content h3,
201+
.post-content h4 {
202+
margin: 2rem 0 0.75rem;
203+
font-weight: 600;
204+
letter-spacing: -0.01em;
205+
}
206+
.post-content h1 { font-size: 1.5rem; }
207+
.post-content h2 { font-size: 1.3rem; }
208+
.post-content h3 { font-size: 1.15rem; }
209+
.post-content p { margin: 0 0 1rem; }
210+
.post-content a { color: var(--foam); text-decoration: underline; }
211+
.post-content a:hover { color: var(--iris); }
212+
.post-content code {
213+
background: var(--surface);
214+
padding: 0.15em 0.4em;
215+
font-size: 0.9em;
216+
border: 1px solid var(--overlay);
217+
font-family: inherit;
218+
}
219+
.post-content pre {
220+
background: var(--surface);
221+
border: 3px solid var(--overlay);
222+
border-radius: var(--radius);
223+
padding: 1rem 1.25rem;
224+
overflow-x: auto;
225+
margin: 0 0 1rem;
226+
}
227+
.post-content pre code {
228+
background: none;
229+
padding: 0;
230+
border: none;
231+
}
232+
.post-content blockquote {
233+
margin: 0 0 1rem;
234+
padding: 0.75rem 1rem;
235+
border-left: 3px solid var(--pine);
236+
background: var(--surface);
237+
color: var(--subtle);
238+
}
239+
.post-content ul,
240+
.post-content ol { margin: 0 0 1rem; padding-left: 1.5rem; }
241+
.post-content li { margin-bottom: 0.25rem; }
242+
.post-content img { max-width: 100%; height: auto; border: 3px solid var(--overlay); border-radius: var(--radius); }
243+
.post-content hr { border: none; border-top: 1px solid var(--overlay); margin: 2rem 0; }
244+
.post-content strong { font-weight: 600; }
245+
246+
footer {
247+
margin-top: 3rem;
248+
padding-top: 1.5rem;
249+
border-top: 1px solid var(--overlay);
250+
color: var(--muted);
251+
font-size: 0.8rem;
252+
text-align: center;
253+
}
254+
footer a { color: var(--pine); text-decoration: none; }
255+
footer a:hover { text-decoration: underline; }
256+
257+
@media (max-width: 480px) {
258+
main { padding: 2.5rem 1rem 4rem; }
259+
h1 { font-size: 1.5rem; }
260+
.post-card a { padding: 1rem 1.25rem; }
261+
}
262+
</style>
263+
</head>
264+
<body>
265+
<main>
266+
<header>
267+
<div>
268+
<h1><a href="/" class="parent-title">thread<span class="ring">locked</span></a><span class="sep">/</span><span class="current-page">blog</span></h1>
269+
<p class="desc">announcements, updates, and ramblings.</p>
270+
</div>
271+
<div class="actions">
272+
<a
273+
class="icon-btn"
274+
href="/"
275+
hx-boost="false"
276+
aria-label="Back to webring"
277+
>
278+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/><polyline points="9 22 9 12 15 12 15 22"/></svg>
279+
</a>
280+
<button id="toggle" class="icon-btn" type="button" aria-label="Toggle theme"></button>
281+
</div>
282+
</header>
283+
284+
<div class="post-list" hx-boost="true"><!--POSTS--></div>
285+
286+
<footer>
287+
<a href="/" hx-boost="false">back to the ring</a> &middot; <a href="/random" hx-boost="false">random site</a> &middot; <a href="/webring" hx-boost="false">json</a>
288+
</footer>
289+
</main>
290+
291+
<script src="https://unpkg.com/htmx.org@2.0.4/dist/htmx.min.js"></script>
292+
<script>
293+
(function () {
294+
var root = document.documentElement;
295+
var btn = document.getElementById("toggle");
296+
var icons = {
297+
dark: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"/></svg>',
298+
light: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="12" r="5"/><line x1="12" y1="1" x2="12" y2="3"/><line x1="12" y1="21" x2="12" y2="23"/><line x1="4.22" y1="4.22" x2="5.64" y2="5.64"/><line x1="18.36" y1="18.36" x2="19.78" y2="19.78"/><line x1="1" y1="12" x2="3" y2="12"/><line x1="21" y1="12" x2="23" y2="12"/><line x1="4.22" y1="19.78" x2="5.64" y2="18.36"/><line x1="18.36" y1="5.64" x2="19.78" y2="4.22"/></svg>',
299+
};
300+
301+
function apply(theme) {
302+
root.dataset.theme = theme;
303+
btn.innerHTML = icons[theme];
304+
try { localStorage.setItem("theme", theme); } catch (e) {}
305+
}
306+
307+
apply(root.dataset.theme || "light");
308+
309+
btn.addEventListener("click", function () {
310+
apply(root.dataset.theme === "dark" ? "light" : "dark");
311+
});
312+
})();
313+
</script>
314+
</body>
315+
</html>

go.mod

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,7 @@ module github.com/0xmukesh/threadlocked
22

33
go 1.26.3
44

5-
require github.com/syumai/workers v0.33.0
5+
require (
6+
github.com/syumai/workers v0.33.0
7+
github.com/yuin/goldmark v1.7.8
8+
)

go.sum

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,4 @@
11
github.com/syumai/workers v0.33.0 h1:ku24TZT8m32leS5wYId1F+pGPVXhl+TPnxaDwbr66y4=
22
github.com/syumai/workers v0.33.0/go.mod h1:ZnqmdiHNBrbxOLrZ/HJ5jzHy6af9cmiNZk10R9NrIEA=
3+
github.com/yuin/goldmark v1.7.8 h1:iERMLn0/QJeHFhxSt3p6PeN9mGnvIKSpG9YYorDMnic=
4+
github.com/yuin/goldmark v1.7.8/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E=

index.html

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,8 +109,10 @@
109109
}
110110
h1 .ring { color: var(--iris); }
111111

112-
.desc { margin: 0; color: var(--subtle); font-size: 1rem; max-width: 52ch; }
112+
.desc { margin: 0; color: var(--subtle); font-size: 1rem; }
113113
.desc .count { color: var(--foam); }
114+
.desc a { color: var(--foam); text-decoration: none; }
115+
.desc a:hover { text-decoration: underline; }
114116

115117
.actions { flex: none; display: flex; gap: 0.5rem; }
116118

@@ -292,6 +294,7 @@ <h1>thread<span class="ring">locked</span></h1>
292294
<p class="desc">
293295
a webring of personal sites.
294296
<span class="count" id="count"><!--COUNT--></span>
297+
&middot; <a href="/blog/">check out our blog</a>
295298
</p>
296299
</div>
297300
<div class="actions">

0 commit comments

Comments
 (0)