Improve styling

- Added font
- Added custom renderer for djot

Signed-off-by: Naomi Roberts <mia@naomieow.xyz>

lesbian.skin 7862512a 03e805b1

verified
+2
gleam.toml
··· 7 7 simplifile = ">= 2.0.0 and < 3.0.0" 8 8 lustre_ssg = ">= 0.8.3 and < 1.0.0" 9 9 tom = ">= 1.1.1 and < 2.0.0" 10 + jot = ">= 4.0.0 and < 5.0.0" 11 + gleam_regexp = ">= 1.1.1 and < 2.0.0" 10 12 11 13 [dev-dependencies] 12 14 gleeunit = ">= 1.0.0 and < 2.0.0"
+2
manifest.toml
··· 23 23 ] 24 24 25 25 [requirements] 26 + gleam_regexp = { version = ">= 1.1.1 and < 2.0.0" } 26 27 gleam_stdlib = { version = ">= 0.60.0 and < 0.70.0" } 27 28 gleeunit = { version = ">= 1.0.0 and < 2.0.0" } 29 + jot = { version = ">= 4.0.0 and < 5.0.0" } 28 30 lustre = { version = ">= 5.0.0 and < 6.0.0" } 29 31 lustre_ssg = { version = ">= 0.8.3 and < 1.0.0" } 30 32 simplifile = { version = ">= 2.0.0 and < 3.0.0" }
+44 -6
src/website/common.gleam
··· 5 5 import lustre/element/html 6 6 import lustre/ssg/djot 7 7 import tom 8 + 9 + // import website/djot_renderer 8 10 import website/style 9 11 10 12 pub type Post(a) { ··· 60 62 ]), 61 63 html.footer([style.footer()], [ 62 64 html.hr([attr.style("color", "lightgrey")]), 63 - .." 64 - The source code for this website is available [here](https://codeberg.org/naomi/website) or [here](https://tangled.sh/@lesbian.skin/website) under the [BSD 3 with Attribution](https://spdx.org/licenses/BSD-3-Clause-Attribution) license. 65 - 66 - If you wish to get in contact, please send an email to [mia@naomieow.xyz](mailto:mia@naomieow.xyz), or contact me on [Discord](https://chat.lesbian.skin). 67 - " 68 - |> djot.render(djot.default_renderer()) 65 + html.p([], [ 66 + html.text("The source code for this website is available "), 67 + link([], "here", "https://codeberg.org/naomi/website", True), 68 + html.text(" or "), 69 + link([], "here", "https://tangled.sh/@lesbian.skin/website", True), 70 + html.text(" under the "), 71 + link( 72 + [], 73 + "BSD 3 with Attribution", 74 + "https://spdx.org/licenses/BSD-3-Clause-Attribution", 75 + True, 76 + ), 77 + html.text(" license."), 78 + ]), 79 + html.p([], [ 80 + html.text("If you wish to get in contact, please send an email to "), 81 + link([], "mia@naomieow.xyz", "mailto:mia@naomieow.xyz", True), 82 + html.text(", or contact me on "), 83 + link([], "Discord", "https://chat.lesbian.skin", True), 84 + html.text("."), 85 + ]), 69 86 ]), 70 87 ]) 71 88 } ··· 146 163 // False -> element.none() 147 164 // }, 148 165 ], 166 + ) 167 + } 168 + 169 + pub fn raw_link( 170 + attributes attributes: List(attr.Attribute(a)), 171 + content content: List(element.Element(a)), 172 + href href: String, 173 + external external: Bool, 174 + ) -> element.Element(a) { 175 + let attrs = [attr.href(href), style.link(), ..attributes] 176 + html.a( 177 + case external { 178 + False -> attrs 179 + True -> [ 180 + attr.target("_blank"), 181 + attr.rel("noopener noreferrer"), 182 + attr.style("display", "inline-block"), 183 + ..attrs 184 + ] 185 + }, 186 + content, 149 187 ) 150 188 } 151 189
+2 -1
src/website/data/posts.gleam
··· 7 7 import simplifile 8 8 import tom 9 9 import website/common.{type Post, Post} 10 + import website/djot_renderer 10 11 11 12 pub fn all() -> List(Post(a)) { 12 13 let path = "./src/website/data/posts/" ··· 32 33 [], 33 34 post_md 34 35 |> djot.content() 35 - |> djot.render(djot.default_renderer()), 36 + |> djot.render(djot_renderer.renderer()), 36 37 ), 37 38 ) 38 39 })
+2 -1
src/website/data/projects.gleam
··· 7 7 import simplifile 8 8 import tom 9 9 import website/common.{type Project, Link, Project} 10 + import website/djot_renderer 10 11 11 12 pub fn all() -> List(Project(a)) { 12 13 let path = "./src/website/data/projects/" ··· 56 57 [], 57 58 project_md 58 59 |> djot.content() 59 - |> djot.render(djot.default_renderer()), 60 + |> djot.render(djot_renderer.renderer()), 60 61 ), 61 62 ) 62 63 })
+104
src/website/djot_renderer.gleam
··· 1 + import gleam/dict 2 + import gleam/list 3 + import gleam/option 4 + import gleam/regexp 5 + import gleam/string 6 + import jot 7 + import lustre/attribute as attr 8 + import lustre/element 9 + import lustre/element/html 10 + import lustre/ssg/djot 11 + import website/common 12 + import website/style 13 + 14 + pub fn renderer() -> djot.Renderer(element.Element(msg)) { 15 + let to_attributes = fn(attrs) { 16 + use attrs, key, val <- dict.fold(attrs, []) 17 + [attr.attribute(key, val), ..attrs] 18 + } 19 + 20 + djot.Renderer( 21 + codeblock: fn(attrs, lang, code) { 22 + let lang = option.unwrap(lang, "text") 23 + html.pre([style.code_block(), ..to_attributes(attrs)], [ 24 + html.code([attr.attribute("data-lang", lang), style.monospaced()], [ 25 + html.text(code), 26 + ]), 27 + ]) 28 + }, 29 + emphasis: fn(content) { html.em([], content) }, 30 + heading: fn(attrs, level, content) { 31 + case level { 32 + 1 -> html.h1(to_attributes(attrs), content) 33 + 2 -> html.h2(to_attributes(attrs), content) 34 + 3 -> html.h3(to_attributes(attrs), content) 35 + 4 -> html.h4(to_attributes(attrs), content) 36 + 5 -> html.h5(to_attributes(attrs), content) 37 + 6 -> html.h6(to_attributes(attrs), content) 38 + _ -> html.p(to_attributes(attrs), content) 39 + } 40 + }, 41 + link: fn(destination, references, content) { 42 + case destination { 43 + jot.Reference(ref) -> 44 + case dict.get(references, ref) { 45 + Ok(url) -> 46 + common.raw_link( 47 + attributes: [], 48 + content:, 49 + href: url, 50 + external: False, 51 + ) 52 + Error(_) -> 53 + common.raw_link( 54 + attributes: [attr.id(linkify("back-to-" <> ref))], 55 + content:, 56 + href: "#" <> linkify(ref), 57 + external: False, 58 + ) 59 + } 60 + jot.Url(url) -> 61 + common.raw_link(attributes: [], content:, href: url, external: False) 62 + } 63 + }, 64 + paragraph: fn(attrs, content) { html.p(to_attributes(attrs), content) }, 65 + bullet_list: fn(layout, style, items) { 66 + let list_style_type = 67 + attr.style("list-style-type", case style { 68 + "-" -> "'-'" 69 + "*" -> "disc" 70 + _ -> "circle" 71 + }) 72 + 73 + html.ul([list_style_type], { 74 + list.map(items, fn(item) { 75 + case layout { 76 + jot.Tight -> html.li([], item) 77 + jot.Loose -> html.li([], [html.p([], item)]) 78 + } 79 + }) 80 + }) 81 + }, 82 + raw_html: fn(content) { element.unsafe_raw_html("", "div", [], content) }, 83 + strong: fn(content) { html.strong([], content) }, 84 + text: fn(text) { html.text(text) }, 85 + code: fn(content) { html.code([style.monospaced()], [html.text(content)]) }, 86 + image: fn(destination, alt) { 87 + case destination { 88 + jot.Reference(ref) -> 89 + html.img([attr.src("#" <> linkify(ref)), attr.alt(alt)]) 90 + jot.Url(url) -> html.img([attr.src(url), attr.alt(alt)]) 91 + } 92 + }, 93 + linebreak: html.br([]), 94 + thematicbreak: html.hr([]), 95 + ) 96 + } 97 + 98 + fn linkify(text: String) -> String { 99 + let assert Ok(re) = regexp.from_string(" +") 100 + 101 + text 102 + |> regexp.split(re, _) 103 + |> string.join("-") 104 + }
+3 -2
src/website/page/post.gleam
··· 4 4 import lustre/ssg/djot 5 5 import website/common.{type Post} 6 6 import website/data/posts 7 + import website/djot_renderer 7 8 import website/style 8 9 9 10 pub fn view_all(posts: List(Post(a))) -> List(Element(a)) { ··· 32 33 ]), 33 34 ]), 34 35 html.div([], [ 35 - html.p([], post.summary |> djot.render(djot.default_renderer())), 36 + html.p([], post.summary |> djot.render(djot_renderer.renderer())), 36 37 ]), 37 38 ]) 38 39 }) ··· 57 58 ]), 58 59 ]), 59 60 html.hr([]), 60 - html.p([], post.summary |> djot.render(djot.default_renderer())), 61 + html.p([], post.summary |> djot.render(djot_renderer.renderer())), 61 62 post.content, 62 63 ]), 63 64 ]
+3 -2
src/website/page/project.gleam
··· 5 5 import lustre/ssg/djot 6 6 import website/common.{type Project, Project} 7 7 import website/data/projects 8 + import website/djot_renderer 8 9 import website/style 9 10 10 11 pub fn view_all(projects: List(Project(a))) -> List(Element(a)) { ··· 35 36 ), 36 37 ]), 37 38 html.div([], [ 38 - html.p([], project.summary |> djot.render(djot.default_renderer())), 39 + html.p([], project.summary |> djot.render(djot_renderer.renderer())), 39 40 ]), 40 41 ]) 41 42 }) ··· 64 65 }), 65 66 ), 66 67 ]), 67 - html.p([], project.summary |> djot.render(djot.default_renderer())), 68 + html.p([], project.summary |> djot.render(djot_renderer.renderer())), 68 69 project.content, 69 70 ]), 70 71 ]
+14 -1
src/website/style.gleam
··· 120 120 pub fn link() { 121 121 attr.styles([ 122 122 #("text-decoration", "underline"), 123 - #("color", "black"), 123 + #("color", "darkblue"), 124 124 #("font-variant-emoji", "text"), 125 125 ]) 126 126 } ··· 195 195 #("gap", "0.5em"), 196 196 ]) 197 197 } 198 + 199 + pub fn monospaced() { 200 + attr.styles([#("font-family", "monospace, monospace")]) 201 + } 202 + 203 + pub fn code_block() { 204 + attr.styles([ 205 + #("font-family", "monospace, monospace"), 206 + #("border-radius", "0.4em"), 207 + #("border", "2px solid black"), 208 + #("padding", "1em 1em 1em 1em"), 209 + ]) 210 + }