💻 My personal website blog.kacaii.dev/
blog gleam lustre

:lipstick: render post title aand descripton

+26 -8
+1
priv/posts/building-my-college-project-with-gleam.md
··· 1 1 --- 2 2 title = "Building my college project with Gleam" 3 + description = "The power of simplicity" 3 4 date = 2025-12-31 4 5 tags = ["gleam", "college"] 5 6 ---
+2 -1
priv/posts/uses.md
··· 1 1 --- 2 - title = "uses" 2 + title = "/uses" 3 + description = "Everything I use for building production-ready systems" 3 4 date = 2026-01-01 4 5 tags = [] 5 6 ---
+12 -3
src/blog/page/content.gleam
··· 8 8 import jot 9 9 import lustre/attribute.{class} 10 10 import lustre/element 11 + import lustre/element/html 11 12 import web 12 13 import wisp 13 14 14 15 pub fn handle_request(ctx: web.Context, post_uri: String) -> wisp.Response { 15 16 case list.find(ctx.posts, fn(post) { post.to_kebab_case(post) == post_uri }) { 16 17 Error(_) -> wisp.not_found() 17 - Ok(found_post) -> { 18 - let title = found_post.meta.title 18 + Ok(post) -> { 19 + let title = post.meta.title 20 + 21 + let post_header = 22 + html.header([], [ 23 + html.h1([class("mb-1 text-4xl font-bold")], [html.text(title)]), 24 + html.p([class("my-2")], [html.text(post.meta.description)]), 25 + html.hr([class("border text-ctp-overlay0")]), 26 + ]) 19 27 20 28 root.view(title:, content: [ 21 29 navbar.view([]), 22 - view(found_post), 30 + post_header, 31 + view(post), 23 32 footer.view(), 24 33 ]) 25 34 }
+1 -1
src/blog/page/posts.gleam
··· 14 14 15 15 pub fn handle_request(ctx: web.Context) -> wisp.Response { 16 16 let ul_styles = class("grid grid-cols-1 gap-4 mx-auto w-full") 17 - let posts = list.filter(ctx.posts, fn(post) { post.meta.title != "uses" }) 17 + let posts = list.filter(ctx.posts, fn(post) { post.meta.title != "/uses" }) 18 18 19 19 let content = [ 20 20 navbar.view([]),
+1 -1
src/blog/page/recent_posts.gleam
··· 8 8 9 9 pub fn view(recent_posts: List(post.Post)) { 10 10 let previews = 11 - list.filter(recent_posts, fn(post) { post.meta.title != "uses" }) 11 + list.filter(recent_posts, fn(post) { post.meta.title != "/uses" }) 12 12 |> list.take(2) 13 13 |> list.map(post_to_li) 14 14
+9 -2
src/blog/post.gleam
··· 22 22 } 23 23 24 24 pub type Metadata { 25 - Metadata(title: String, date: calendar.Date, tags: List(String)) 25 + Metadata( 26 + title: String, 27 + description: String, 28 + date: calendar.Date, 29 + tags: List(String), 30 + ) 26 31 } 27 32 28 33 pub fn to_kebab_case(post: Post) -> String { ··· 35 40 |> string.replace("]", "") 36 41 |> string.replace("{", "") 37 42 |> string.replace("}", "") 43 + |> string.replace("/", "") 38 44 } 39 45 40 46 pub fn parse(path file: String) -> Result(Post, PostError) { ··· 61 67 ) 62 68 63 69 use title <- toml_field(toml, tom.get_string, "title") 70 + use description <- toml_field(toml, tom.get_string, "description") 64 71 use date <- toml_field(toml, tom.get_date, "date") 65 72 use toml_tags <- toml_field(toml, tom.get_array, "tags") 66 73 use tags <- result.map(parse_tags(toml_tags)) 67 74 68 - Metadata(title:, date:, tags:) 75 + Metadata(title:, description:, date:, tags:) 69 76 } 70 77 71 78 fn parse_tags(tags: List(tom.Toml)) -> Result(List(String), PostError) {