tangled
alpha
login
or
join now
oppi.li
/
gleam-ci-tests
forked from
lesbian.skin/website
0
fork
atom
not my website
0
fork
atom
overview
issues
pulls
pipelines
CSS Refactor + Dark Mode
lesbian.skin
2 years ago
641d010a
e6d0ead2
+260
-82
5 changed files
expand all
collapse all
unified
split
index.html
src
ffi.mjs
website
common.gleam
style.gleam
website.gleam
+1
-1
index.html
···
13
<script type="module" src="/priv/static/website.mjs"></script>
14
</head>
15
16
-
<body>
17
<div id="app"></div>
18
</body>
19
</html>
···
13
<script type="module" src="/priv/static/website.mjs"></script>
14
</head>
15
16
+
<body style="margin: 0;">
17
<div id="app"></div>
18
</body>
19
</html>
+4
src/ffi.mjs
···
1
export function get_route() {
2
return window.location.pathname
0
0
0
0
3
}
···
1
export function get_route() {
2
return window.location.pathname
3
+
}
4
+
5
+
export function prefers_dark_mode() {
6
+
return window.matchMedia('(prefers-color-scheme:dark)').matches
7
}
+74
-73
src/website.gleam
···
1
-
import birl.{type Day}
2
-
import gleam/int
3
import gleam/list
4
import gleam/string
5
import gleam/uri.{type Uri}
···
8
import lustre/effect.{type Effect}
9
import lustre/element.{type Element}
10
import lustre/element/html
0
11
import lustre/ui
12
import lustre/ui/layout/cluster
13
import modem
14
import website/common
15
import website/posts
16
import website/projects
0
17
18
// Main
19
···
31
current_route: Route,
32
posts: List(posts.Post(Msg)),
33
projects: List(projects.Project(Msg)),
0
34
)
35
}
36
···
45
@external(javascript, "./ffi.mjs", "get_route")
46
fn do_get_route() -> String
47
0
0
0
48
fn get_route() -> Route {
49
case do_get_route() |> string.split("/") {
50
["", "projects", id] | ["", "project", id] -> Project(id)
···
61
current_route: get_route(),
62
projects: projects.all(),
63
posts: posts.all(),
0
64
),
65
modem.init(on_route_change),
66
)
···
80
81
pub opaque type Msg {
82
OnRouteChange(Route)
0
83
}
84
85
fn update(model: Model, msg: Msg) -> #(Model, Effect(Msg)) {
···
88
Model(..model, current_route: route),
89
effect.none(),
90
)
0
0
0
0
91
}
92
}
93
94
// View
95
96
fn view(model: Model) -> Element(Msg) {
97
-
let styles = [
98
-
#("margin-left", "25vh"),
99
-
#("margin-top", "3vh"),
100
-
#("margin-right", "25vh"),
101
-
#("font-family", "\"Inter\", sans-serif"),
102
-
#("font-weight", "400"),
103
-
#("font-style", "normal"),
104
-
]
105
-
106
let page = case model.current_route {
107
Home -> view_home(model)
108
Projects -> view_projects(model)
···
111
Post(id) -> view_post(model, id)
112
}
113
114
-
ui.stack([attribute.style(styles)], [view_navbar(model), page])
0
0
115
}
116
117
-
const navbar_style: List(#(String, String)) = [
118
-
#("display", "flex"), #("background-color", "#cae4e7"),
119
-
#("border-radius", "1em"), #("padding", "1em"), #("margin-bottom", "3vh"),
120
-
#("justify-conten", "center"), #("align-items", "center"), #("gap", "1em"),
121
-
]
122
-
123
-
const navitem_style: List(#(String, String)) = [
124
-
#("text-decoration", "none"), #("background-color", "#b8cfd2"),
125
-
#("padding", "0.75em"), #("border-radius", "1em"), #("color", "black"),
126
-
]
127
128
-
fn view_navbar(_) -> Element(Msg) {
129
let view_nav_item = fn(path, text) {
130
html.a([attribute.href("/" <> path), attribute.style(navitem_style)], [
131
element.text(text),
132
])
133
}
134
135
-
cluster.of(html.nav, [attribute.style(navbar_style)], [
136
-
html.h1([attribute.style([#("margin", "0")])], [element.text("naomieow")]),
137
-
view_nav_item("", "Home"),
138
-
view_nav_item("projects", "Projects"),
139
-
view_nav_item("posts", "Posts"),
140
-
])
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
141
}
142
143
-
const home_style: List(#(String, String)) = [
144
-
#("background-color", "#cae4e7"), #("border-radius", "1em"),
145
-
#("padding", "1em"), #("margin-bottom", "1em"), #("font-size", "1.25em"),
146
-
]
147
148
-
fn view_home(_model: Model) -> Element(Msg) {
149
html.div([attribute.style(home_style)], [
150
html.p([], [
151
element.text(
···
197
],
198
),
199
]),
200
-
// html.p([], [
201
// element.text("As of this coming September, I will be studying a BSc in Computer Games Programming @ "),
202
// common.link("Staffs", "https://staffs.ac.uk/")
203
// ])
204
])
205
}
206
207
-
const icon_style: List(#(String, String)) = [
208
-
#("max-width", "4em"), #("max-height", "4em"), #("border-radius", "1em"),
209
-
]
210
-
211
-
const project_style: List(#(String, String)) = [
212
-
#("background-color", "#cae4e7"), #("border-radius", "1em"),
213
-
#("padding", "1em"), #("margin-bottom", "1em"),
214
-
]
215
-
216
-
const project_bar_style: List(#(String, String)) = [
217
-
#("display", "flex"), #("align-items", "center"), #("gap", "1em"),
218
-
]
219
-
220
fn view_projects(model: Model) -> Element(Msg) {
0
221
let projects =
222
model.projects
223
|> list.map(fn(project: projects.Project(Msg)) {
224
ui.stack(
225
[attribute.id("project-" <> project.id), attribute.style(project_style)],
226
[
227
-
cluster.of(html.div, [attribute.style(project_bar_style)], [
228
-
html.img([attribute.src(project.img), attribute.style(icon_style)]),
229
-
html.h1([], [
230
-
common.styled_link(project.title, "projects/" <> project.id),
231
-
]),
232
cluster.of(
233
html.div,
234
-
[attribute.style(project_bar_style)],
235
project.links
236
|> list.map(fn(link) {
237
html.a([attribute.href(link.url), attribute.alt(link.title)], [
238
html.img([
239
attribute.src(link.img),
240
-
attribute.style(icon_style),
241
]),
242
])
243
}),
···
251
}
252
253
fn view_project(model: Model, id: String) -> Element(Msg) {
0
254
let project =
255
model.projects
256
|> list.find(fn(project) { project.id == id })
257
html.div([attribute.style(project_style)], case project {
258
Ok(project) -> [
259
-
cluster.of(html.div, [attribute.style(project_bar_style)], [
260
-
html.img([attribute.src(project.img), attribute.style(icon_style)]),
261
html.h1([], [element.text(project.title)]),
262
]),
263
html.p([], [project.summary]),
···
271
})
272
}
273
274
-
const post_style: List(#(String, String)) = [
275
-
#("background-color", "#cae4e7"), #("border-radius", "1em"),
276
-
#("padding", "1em"), #("margin-bottom", "1em"),
277
-
]
278
-
279
-
fn day_to_string(day: Day) -> String {
280
-
day.date |> int.to_string
281
-
<> "/"
282
-
<> day.month |> int.to_string
283
-
<> "/"
284
-
<> day.year |> int.to_string
285
-
}
286
287
-
fn view_posts(model: Model) -> Element(Msg) {
288
let posts =
289
model.posts
290
|> list.map(fn(post: posts.Post(Msg)) {
···
299
]),
300
],
301
[
302
-
html.h1([], [common.styled_link(post.title, "posts/" <> post.id)]),
303
html.p([attribute.style([#("color", "gray")])], [
304
element.text("Author: " <> post.author),
305
]),
306
html.p([attribute.style([#("color", "gray")])], [
307
-
element.text("Date Posted: " <> post.date_posted |> day_to_string),
0
0
308
]),
309
],
310
),
···
315
}
316
317
fn view_post(model: Model, id: String) -> Element(Msg) {
0
0
318
let post =
319
model.posts
320
|> list.find(fn(post) { post.id == id })
···
335
element.text("Author: " <> post.author),
336
]),
337
html.p([attribute.style([#("color", "gray")])], [
338
-
element.text("Date Posted: " <> post.date_posted |> day_to_string),
0
0
339
]),
340
],
341
),
···
0
0
1
import gleam/list
2
import gleam/string
3
import gleam/uri.{type Uri}
···
6
import lustre/effect.{type Effect}
7
import lustre/element.{type Element}
8
import lustre/element/html
9
+
import lustre/event
10
import lustre/ui
11
import lustre/ui/layout/cluster
12
import modem
13
import website/common
14
import website/posts
15
import website/projects
16
+
import website/style
17
18
// Main
19
···
31
current_route: Route,
32
posts: List(posts.Post(Msg)),
33
projects: List(projects.Project(Msg)),
34
+
dark_mode: Bool,
35
)
36
}
37
···
46
@external(javascript, "./ffi.mjs", "get_route")
47
fn do_get_route() -> String
48
49
+
@external(javascript, "./ffi.mjs", "prefers_dark_mode")
50
+
fn prefers_dark_mode() -> Bool
51
+
52
fn get_route() -> Route {
53
case do_get_route() |> string.split("/") {
54
["", "projects", id] | ["", "project", id] -> Project(id)
···
65
current_route: get_route(),
66
projects: projects.all(),
67
posts: posts.all(),
68
+
dark_mode: prefers_dark_mode(),
69
),
70
modem.init(on_route_change),
71
)
···
85
86
pub opaque type Msg {
87
OnRouteChange(Route)
88
+
ChangeDarkMode
89
}
90
91
fn update(model: Model, msg: Msg) -> #(Model, Effect(Msg)) {
···
94
Model(..model, current_route: route),
95
effect.none(),
96
)
97
+
ChangeDarkMode -> #(
98
+
Model(..model, dark_mode: !model.dark_mode),
99
+
effect.none(),
100
+
)
101
}
102
}
103
104
// View
105
106
fn view(model: Model) -> Element(Msg) {
0
0
0
0
0
0
0
0
0
107
let page = case model.current_route {
108
Home -> view_home(model)
109
Projects -> view_projects(model)
···
112
Post(id) -> view_post(model, id)
113
}
114
115
+
ui.stack([attribute.style(style.page(model.dark_mode))], [
116
+
html.div([attribute.style(style.page_padding)], [view_navbar(model), page]),
117
+
])
118
}
119
120
+
fn view_navbar(model: Model) -> Element(Msg) {
121
+
let navitem_style = style.navitem(model.dark_mode)
122
+
let navbar_style = style.navbar(model.dark_mode)
0
0
0
0
0
0
0
123
0
124
let view_nav_item = fn(path, text) {
125
html.a([attribute.href("/" <> path), attribute.style(navitem_style)], [
126
element.text(text),
127
])
128
}
129
130
+
html.span(
131
+
[
132
+
attribute.style([
133
+
#("display", "flex"),
134
+
#("gap", "1em"),
135
+
#("width", "100%"),
136
+
]),
137
+
],
138
+
[
139
+
cluster.of(html.nav, [attribute.style(navbar_style)], [
140
+
html.h1([attribute.style([#("margin", "0"), #("color", "#f380b1")])], [
141
+
element.text("naomieow"),
142
+
]),
143
+
view_nav_item("", "Home"),
144
+
view_nav_item("projects", "Projects"),
145
+
view_nav_item("posts", "Posts"),
146
+
]),
147
+
ui.button(
148
+
[
149
+
attribute.style(style.button(model.dark_mode)),
150
+
event.on_click(ChangeDarkMode),
151
+
],
152
+
[
153
+
html.h1([attribute.style([#("margin", "0")])], [
154
+
case model.dark_mode {
155
+
True -> element.text("☀️")
156
+
False -> element.text("🌕")
157
+
},
158
+
]),
159
+
],
160
+
),
161
+
],
162
+
)
163
}
164
165
+
fn view_home(model: Model) -> Element(Msg) {
166
+
let home_style = style.home(model.dark_mode)
0
0
167
0
168
html.div([attribute.style(home_style)], [
169
html.p([], [
170
element.text(
···
216
],
217
),
218
]),
219
+
// html.p([], [
220
// element.text("As of this coming September, I will be studying a BSc in Computer Games Programming @ "),
221
// common.link("Staffs", "https://staffs.ac.uk/")
222
// ])
223
])
224
}
225
0
0
0
0
0
0
0
0
0
0
0
0
0
226
fn view_projects(model: Model) -> Element(Msg) {
227
+
let project_style = style.project(model.dark_mode)
228
let projects =
229
model.projects
230
|> list.map(fn(project: projects.Project(Msg)) {
231
ui.stack(
232
[attribute.id("project-" <> project.id), attribute.style(project_style)],
233
[
234
+
cluster.of(html.div, [attribute.style(style.project_bar)], [
235
+
html.img([attribute.src(project.img), attribute.style(style.icon)]),
236
+
html.h1([], [common.link(project.title, "projects/" <> project.id)]),
0
0
237
cluster.of(
238
html.div,
239
+
[attribute.style(style.project_bar)],
240
project.links
241
|> list.map(fn(link) {
242
html.a([attribute.href(link.url), attribute.alt(link.title)], [
243
html.img([
244
attribute.src(link.img),
245
+
attribute.style(style.icon),
246
]),
247
])
248
}),
···
256
}
257
258
fn view_project(model: Model, id: String) -> Element(Msg) {
259
+
let project_style = style.project(model.dark_mode)
260
let project =
261
model.projects
262
|> list.find(fn(project) { project.id == id })
263
html.div([attribute.style(project_style)], case project {
264
Ok(project) -> [
265
+
cluster.of(html.div, [attribute.style(style.project_bar)], [
266
+
html.img([attribute.src(project.img), attribute.style(style.icon)]),
267
html.h1([], [element.text(project.title)]),
268
]),
269
html.p([], [project.summary]),
···
277
})
278
}
279
280
+
fn view_posts(model: Model) -> Element(Msg) {
281
+
let post_style = style.post(model.dark_mode)
0
0
0
0
0
0
0
0
0
0
282
0
283
let posts =
284
model.posts
285
|> list.map(fn(post: posts.Post(Msg)) {
···
294
]),
295
],
296
[
297
+
html.h1([], [common.link(post.title, "posts/" <> post.id)]),
298
html.p([attribute.style([#("color", "gray")])], [
299
element.text("Author: " <> post.author),
300
]),
301
html.p([attribute.style([#("color", "gray")])], [
302
+
element.text(
303
+
"Date Posted: " <> post.date_posted |> common.day_to_string,
304
+
),
305
]),
306
],
307
),
···
312
}
313
314
fn view_post(model: Model, id: String) -> Element(Msg) {
315
+
let post_style = style.post(model.dark_mode)
316
+
317
let post =
318
model.posts
319
|> list.find(fn(post) { post.id == id })
···
334
element.text("Author: " <> post.author),
335
]),
336
html.p([attribute.style([#("color", "gray")])], [
337
+
element.text(
338
+
"Date Posted: " <> post.date_posted |> common.day_to_string,
339
+
),
340
]),
341
],
342
),
+12
-8
src/website/common.gleam
···
0
0
1
import lustre/attribute as attr
2
import lustre/element
3
import lustre/element/html
4
5
-
const link_style: List(#(String, String)) = [
6
-
#("text-decoration", "underline"), #("color", "black"),
7
-
]
8
-
9
-
pub fn styled_link(text: String, link: String) -> element.Element(a) {
10
-
html.a([attr.href(link), attr.style(link_style)], [element.text(text)])
11
}
12
13
-
pub fn link(text: String, link: String) -> element.Element(a) {
14
-
html.a([attr.href(link)], [element.text(text)])
0
0
0
0
15
}
···
1
+
import birl.{type Day}
2
+
import gleam/int
3
import lustre/attribute as attr
4
import lustre/element
5
import lustre/element/html
6
7
+
pub fn link(text: String, link: String) -> element.Element(a) {
8
+
html.a([attr.href(link), attr.style([#("color", "#f380b1")])], [
9
+
element.text(text),
10
+
])
0
0
11
}
12
13
+
pub fn day_to_string(day: Day) -> String {
14
+
day.date |> int.to_string
15
+
<> "/"
16
+
<> day.month |> int.to_string
17
+
<> "/"
18
+
<> day.year |> int.to_string
19
}
+169
src/website/style.gleam
···
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
···
1
+
pub const page_padding: List(#(String, String)) = [
2
+
#("margin-left", "25vh"), #("margin-right", "25vh"),
3
+
]
4
+
5
+
pub fn page(dark_mode: Bool) -> List(#(String, String)) {
6
+
case dark_mode {
7
+
True -> [
8
+
#("font-family", "\"Inter\", sans-serif"),
9
+
#("font-weight", "400"),
10
+
#("font-style", "normal"),
11
+
#("min-height", "100vh"),
12
+
#("background-color", "#151515"),
13
+
#("padding", "3vh"),
14
+
]
15
+
False -> [
16
+
#("font-family", "\"Inter\", sans-serif"),
17
+
#("font-weight", "400"),
18
+
#("font-style", "normal"),
19
+
#("min-height", "100vh"),
20
+
#("padding", "3vh"),
21
+
]
22
+
}
23
+
}
24
+
25
+
pub fn navbar(dark_mode: Bool) -> List(#(String, String)) {
26
+
case dark_mode {
27
+
True -> [
28
+
#("display", "flex"),
29
+
#("background-color", "#1e1e1e"),
30
+
#("border-radius", "1em"),
31
+
#("padding", "1em"),
32
+
#("margin-bottom", "3vh"),
33
+
#("justify-conten", "center"),
34
+
#("align-items", "center"),
35
+
#("gap", "1em"),
36
+
#("border", "0"),
37
+
#("flex-grow", "1"),
38
+
]
39
+
False -> [
40
+
#("display", "flex"),
41
+
#("background-color", "#cae4e7"),
42
+
#("border-radius", "1em"),
43
+
#("padding", "1em"),
44
+
#("margin-bottom", "3vh"),
45
+
#("justify-conten", "center"),
46
+
#("align-items", "center"),
47
+
#("gap", "1em"),
48
+
#("border", "0"),
49
+
#("flex-grow", "1"),
50
+
]
51
+
}
52
+
}
53
+
54
+
pub fn button(dark_mode: Bool) -> List(#(String, String)) {
55
+
case dark_mode {
56
+
True -> [
57
+
#("background-color", "#1e1e1e"),
58
+
#("border-radius", "1em"),
59
+
#("padding", "1em"),
60
+
#("margin-bottom", "3vh"),
61
+
#("justify-conten", "center"),
62
+
#("align-items", "center"),
63
+
#("aspect-ratio", "1 / 1"),
64
+
#("border", "0"),
65
+
#("width", "6%")
66
+
]
67
+
False -> [
68
+
#("background-color", "#cae4e7"),
69
+
#("border-radius", "1em"),
70
+
#("padding", "1em"),
71
+
#("margin-bottom", "3vh"),
72
+
#("justify-conten", "center"),
73
+
#("align-items", "center"),
74
+
#("aspect-ratio", "1 / 1"),
75
+
#("border", "0"),
76
+
#("width", "6%")
77
+
]
78
+
}
79
+
}
80
+
81
+
pub fn navitem(dark_mode: Bool) -> List(#(String, String)) {
82
+
case dark_mode {
83
+
True -> [
84
+
#("text-decoration", "none"),
85
+
#("background-color", "#151515"),
86
+
#("padding", "0.75em"),
87
+
#("border-radius", "1em"),
88
+
#("color", "#dcdcdc"),
89
+
]
90
+
False -> [
91
+
#("text-decoration", "none"),
92
+
#("background-color", "#b8cfd2"),
93
+
#("padding", "0.75em"),
94
+
#("border-radius", "1em"),
95
+
#("color", "black"),
96
+
]
97
+
}
98
+
}
99
+
100
+
pub fn home(dark_mode: Bool) -> List(#(String, String)) {
101
+
case dark_mode {
102
+
True -> [
103
+
#("background-color", "#1e1e1e"),
104
+
#("border-radius", "1em"),
105
+
#("padding", "1em"),
106
+
#("margin-bottom", "1em"),
107
+
#("font-size", "1.25em"),
108
+
#("color", "#dcdcdc"),
109
+
]
110
+
False -> [
111
+
#("background-color", "#cae4e7"),
112
+
#("border-radius", "1em"),
113
+
#("padding", "1em"),
114
+
#("margin-bottom", "1em"),
115
+
#("font-size", "1.25em"),
116
+
]
117
+
}
118
+
}
119
+
120
+
pub const icon: List(#(String, String)) = [
121
+
#("max-width", "4em"), #("max-height", "4em"), #("border-radius", "1em"),
122
+
]
123
+
124
+
pub fn project(dark_mode: Bool) -> List(#(String, String)) {
125
+
case dark_mode {
126
+
True -> [
127
+
#("background-color", "#1e1e1e"),
128
+
#("border-radius", "1em"),
129
+
#("padding", "1em"),
130
+
#("margin-bottom", "1em"),
131
+
#("color", "#dcdcdc"),
132
+
]
133
+
False -> [
134
+
#("background-color", "#cae4e7"),
135
+
#("border-radius", "1em"),
136
+
#("padding", "1em"),
137
+
#("margin-bottom", "1em"),
138
+
]
139
+
}
140
+
}
141
+
142
+
pub const project_bar: List(#(String, String)) = [
143
+
#("display", "flex"), #("align-items", "center"), #("gap", "1em"),
144
+
]
145
+
146
+
pub fn post(dark_mode: Bool) -> List(#(String, String)) {
147
+
case dark_mode {
148
+
True -> [
149
+
#("background-color", "#1e1e1e"),
150
+
#("border-radius", "1em"),
151
+
#("padding", "1em"),
152
+
#("margin-bottom", "1em"),
153
+
#("color", "#dcdcdc"),
154
+
]
155
+
False -> [
156
+
#("background-color", "#cae4e7"),
157
+
#("border-radius", "1em"),
158
+
#("padding", "1em"),
159
+
#("margin-bottom", "1em"),
160
+
]
161
+
}
162
+
}
163
+
164
+
pub fn link(dark_mode: Bool) -> List(#(String, String)) {
165
+
case dark_mode {
166
+
True -> [#("text-decoration", "underline"), #("color", "#dcdcdc")]
167
+
False -> [#("text-decoration", "underline"), #("color", "black")]
168
+
}
169
+
}