my page
ollie.earth
1import blogatto
2import blogatto/config
3import blogatto/config/markdown
4import blogatto/config/robots
5import blogatto/post
6import glaze/oat/badge
7import glaze/oat/theme
8import gleam/list
9import gleam/option.{None, Some}
10import gleam/time/timestamp
11import lustre/attribute.{attribute}
12import lustre/element.{type Element}
13import lustre/element/html
14import page/svgs
15
16pub fn main() -> Nil {
17 let assert Ok(Nil) = blogatto.build(config())
18 Nil
19}
20
21pub fn config() -> config.Config(Nil) {
22 let robots =
23 robots.new("https://ollie.earth")
24 |> robots.robot(
25 robots.Robot(user_agent: "*", allowed_routes: [], disallowed_routes: [
26 "/",
27 "*",
28 "/*",
29 ]),
30 )
31
32 let md =
33 markdown.default()
34 |> markdown.markdown_path("./blog")
35 |> markdown.route_prefix("blog")
36 |> markdown.template(fn(post, _posts) { page(post.contents) })
37
38 config.new("https://page.ollie.earth")
39 |> config.output_dir("./dist")
40 |> config.static_dir("./static")
41 |> config.markdown(md)
42 |> config.robots(robots)
43 |> config.route("/", home_view)
44}
45
46fn page(content) {
47 html.html([attribute.lang("en")], [
48 html.head([], [
49 html.meta([
50 attribute.name("viewport"),
51 attribute.content("width=device-width, initial-scale=1.0"),
52 ]),
53
54 html.meta([
55 attribute.content("ollie@sharkey.mar.ollie.earth"),
56 attribute.name("fediverse:creator"),
57 ]),
58
59 html.link([
60 attribute.rel("me"),
61 attribute.href("https://sharkey.mar.ollie.earth/@ollie"),
62 ]),
63
64 html.title([], "ollie.earth"),
65 html.link([
66 attribute.rel("icon"),
67 attribute.href("/media/small_snoot_round.png"),
68 ]),
69
70 html.link([
71 attribute.rel("stylesheet"),
72 attribute.href("/oat.min.css"),
73 ]),
74 html.script(
75 [
76 attribute.attribute("defer", "defer"),
77 attribute.src("/oat.min.js"),
78 ],
79 "",
80 ),
81 font(),
82 theme.style_tag(
83 theme.default_theme()
84 |> theme.set(theme.Background, "light-dark(#f9f6f1, #09090b)")
85 |> theme.set(theme.Foreground, "light-dark(#f67cbc, #fafafa)")
86 |> theme.set(theme.Primary, "light-dark(#9ceca6, #fafafa)")
87 |> theme.set(theme.PrimaryForeground, "light-dark(#624226, #18181b)")
88 |> theme.set(theme.Muted, "light-dark(#f9f6f1, #27272a)")
89 |> theme.set(theme.MutedForeground, "light-dark(#ffc8e8, #a1a1aa)")
90 |> theme.set(theme.FontSans, "Papernotes")
91 |> theme.set(theme.FontMono, "Papernotes"),
92 ),
93 html.link([
94 attribute.rel("stylesheet"),
95 attribute.href("/style.css"),
96 ]),
97 ]),
98 html.body([], [
99 html.div(
100 [attribute.class("container"), attribute.style("max-width", "850px")],
101 content,
102 ),
103 ]),
104 ])
105}
106
107fn home_view(posts: List(post.Post(Nil))) -> Element(Nil) {
108 let sorted = list.sort(posts, fn(a, b) { timestamp.compare(b.date, a.date) })
109
110 page([
111 html.nav([attribute.attribute("data-topnav", "")], [
112 html.div([attribute.class("hstack")], [
113 html.div([attribute.class("hstack")], [
114 link_icon(
115 to: "ollie.earth",
116 at: "https://ollie.earth",
117 with_image: "/media/small_snoot_round.png",
118 size: Small,
119 scaling: None,
120 ),
121 html.div([attribute.style("font-size", "var(--text-2)")], [
122 element.text("ollie.earth"),
123 ]),
124 ]),
125 html.div(
126 [
127 attribute.class("hstack hide-s"),
128 ],
129 [
130 html.a([attribute.href("https://en.pronouns.page/@liiv")], [
131 badge.badge([], [element.text("pronouns")]),
132 ]),
133 html.a([attribute.href("https://www.last.fm/user/nnuuvv")], [
134 badge.badge([], [element.text("lastfm")]),
135 ]),
136 ],
137 ),
138 // socials(Some("hide-md"), Size(26, 26)),
139 ]),
140 ]),
141 html.main([], [
142 html.div([attribute.class("container")], [
143 html.h3([], [element.text("hi there!")]),
144 html.p([], [
145 element.text("im ollie, a developer from germany"),
146 html.br([]),
147 element.text("welcome to my little corner of the internet"),
148 ]),
149 html.p([], [
150 element.text(
151 "i enjoy backend development, devops, self hosting and, these days, occasionally gaming",
152 ),
153 html.br([]),
154 element.text(
155 "you can find my public facing services below (the ones i remembered to put here at least...)",
156 ),
157 ]),
158
159 html.hr([]),
160
161 html.section([], [
162 html.h3([], [element.text("stuff hosted by me")]),
163 html.div([attribute.class("hstack")], [
164 svgs.services(None, svgs.Size(3.25, 3.25)),
165 ]),
166 ]),
167
168 html.hr([]),
169
170 html.section([], [
171 html.h3([], [element.text("people and projects i like")]),
172 html.div([attribute.class("hstack")], [
173 link_icon(
174 to: "mar",
175 at: "https://strawmelonjuice.com",
176 with_image: "https://strawmelonjuice.com/strawmelonjuice.svg",
177 size: Large,
178 scaling: None,
179 ),
180 link_icon(
181 to: "gleam",
182 at: "https://gleam.run",
183 with_image: "/media/lucy.svg",
184 size: Large,
185 scaling: Some(Scaling(Some("auto"), Some("85%"))),
186 ),
187 link_icon(
188 to: "lustre",
189 at: "https://github.com/lustre-labs",
190 with_image: "/media/lustre.png",
191 size: Large,
192 scaling: None,
193 ),
194 // louis
195 // giacomo
196 // hayleigh
197 // mar
198 // gleam
199 // lustre
200 ]),
201 ]),
202
203 html.hr([]),
204
205 html.section([], [
206 html.h3([], [element.text("me in other places")]),
207 svgs.socials(None, svgs.Size(3.25, 3.25)),
208 ]),
209
210 html.hr([]),
211
212 html.h3([], [
213 element.text("this is where id put my blog posts..."),
214 ]),
215 element.text("IF I HAD ANY!"),
216
217 // html.img([attribute.src("/media/ref.png")]),
218 // html.img([attribute.src("/media/puppy.png")]),
219 html.ul(
220 [],
221 list.map(sorted, fn(p) {
222 html.li([], [
223 html.a([attribute.href("/blog/" <> p.slug)], [
224 element.text(p.title),
225 ]),
226 ])
227 }),
228 ),
229
230 html.footer([attribute.class("footer text-light")], [
231 html.p([], [
232 element.text("made with "),
233 html.a([attribute.href("https://gleam.run")], [
234 element.text("gleam"),
235 ]),
236 element.text(" using "),
237 html.a([attribute.href("https://blogat.to")], [
238 element.text("blogatto"),
239 ]),
240 element.text(", "),
241 html.a([attribute.href("https://oat.ink")], [
242 element.text("oat ui"),
243 ]),
244
245 element.text(" and "),
246 html.a(
247 [
248 attribute.href(
249 "https://github.com/daniellionel01/glaze/tree/main/glaze_oat",
250 ),
251 ],
252 [
253 element.text("glaze_oat"),
254 ],
255 ),
256 ]),
257
258 html.div([attribute.class("carbonbadge"), attribute.id("wcb")], []),
259 html.script(
260 [
261 attribute("defer", ""),
262 attribute.src(
263 "https://unpkg.com/website-carbon-badges@1.1.3/b.min.js",
264 ),
265 ],
266 "",
267 ),
268 ]),
269 ]),
270 ]),
271 ])
272}
273
274fn font() -> Element(Nil) {
275 html.style(
276 [attribute.type_("text/css")],
277 "
278@font-face {
279 font-family: \"Papernotes\";
280 font-display: swap;
281 src: url('/fonts/Papernotes.woff2') format(\"woff2\");
282}
283
284* {
285 font-family: \"Papernotes\" !important;
286}
287",
288 )
289}
290
291type Size {
292 Small
293 Normal
294 Large
295}
296
297type Scaling {
298 Scaling(width: option.Option(String), height: option.Option(String))
299}
300
301fn scaling_to_style(scaling: Scaling) -> attribute.Attribute(a) {
302 let Scaling(width:, height:) = scaling
303
304 let width = width |> option.unwrap("100%")
305 let height = height |> option.unwrap("100%")
306
307 attribute.styles([#("width", width), #("height", height)])
308}
309
310fn link_icon(
311 to target_name: String,
312 at target_link: String,
313 with_image image_source: String,
314 size size: Size,
315 scaling scaling: option.Option(Scaling),
316) -> Element(_) {
317 html.a(
318 [
319 attribute.href(target_link),
320 attribute.target("_blank"),
321 attribute.style("width", "fit-content"),
322 attribute.style("height", "fit-content"),
323 ],
324 [
325 html.figure(
326 [
327 attribute.attribute("data-variant", "avatar"),
328 attribute.style("display", "flex"),
329 attribute.class(case size {
330 Small -> "small"
331 Normal -> "normal"
332 Large -> "large"
333 }),
334 attribute.aria_label(target_name),
335 ],
336 [
337 html.img([
338 option.map(scaling, scaling_to_style)
339 |> option.unwrap(attribute.none()),
340 attribute.src(image_source),
341 attribute.alt(target_name),
342 ]),
343 ],
344 ),
345 ],
346 )
347}