From 8c199ac34bd80aeb241412a967d4202cacf5a69b Mon Sep 17 00:00:00 2001 From: Louis Pilfold Date: Wed, 13 Mar 2024 21:17:32 +0000 Subject: Make content on single page (needs CSS and new name) --- src/icons.gleam | 61 ---------------- src/tour.gleam | 189 +++++++++++++++++++++++++++---------------------- src/tour/widgets.gleam | 107 ++++++++++++++++++++++++++++ 3 files changed, 211 insertions(+), 146 deletions(-) delete mode 100644 src/icons.gleam create mode 100644 src/tour/widgets.gleam (limited to 'src') diff --git a/src/icons.gleam b/src/icons.gleam deleted file mode 100644 index 034d7c3..0000000 --- a/src/icons.gleam +++ /dev/null @@ -1,61 +0,0 @@ -import htmb.{type Html, h} - -pub fn icon_moon() -> Html { - h("svg", [#("id", "icon-moon"), #("viewBox", "0 0 24 24")], [ - h( - "path", - [ - #( - "d", - "M21.996 12.882c0.022-0.233-0.038-0.476-0.188-0.681-0.325-0.446-0.951-0.544-1.397-0.219-0.95 0.693-2.060 1.086-3.188 1.162-1.368 0.092-2.765-0.283-3.95-1.158-1.333-0.985-2.139-2.415-2.367-3.935s0.124-3.124 1.109-4.456c0.142-0.191 0.216-0.435 0.191-0.691-0.053-0.55-0.542-0.952-1.092-0.898-2.258 0.22-4.314 1.18-5.895 2.651-1.736 1.615-2.902 3.847-3.137 6.386-0.254 2.749 0.631 5.343 2.266 7.311s4.022 3.313 6.772 3.567 5.343-0.631 7.311-2.266 3.313-4.022 3.567-6.772zM19.567 14.674c-0.49 1.363-1.335 2.543-2.416 3.441-1.576 1.309-3.648 2.016-5.848 1.813s-4.108-1.278-5.417-2.854-2.016-3.648-1.813-5.848c0.187-2.032 1.117-3.814 2.507-5.106 0.782-0.728 1.71-1.3 2.731-1.672-0.456 1.264-0.577 2.606-0.384 3.899 0.303 2.023 1.38 3.934 3.156 5.247 1.578 1.167 3.448 1.668 5.272 1.545 0.752-0.050 1.496-0.207 2.21-0.465z", - ), - ], - [], - ), - ]) -} - -pub fn icon_sun() -> Html { - h("svg", [#("id", "icon-sun"), #("viewBox", "0 0 24 24")], [ - h( - "path", - [ - #( - "d", - "M18 12c0-1.657-0.673-3.158-1.757-4.243s-2.586-1.757-4.243-1.757-3.158 0.673-4.243 1.757-1.757 2.586-1.757 4.243 0.673 3.158 1.757 4.243 2.586 1.757 4.243 1.757 3.158-0.673 4.243-1.757 1.757-2.586 1.757-4.243zM16 12c0 1.105-0.447 2.103-1.172 2.828s-1.723 1.172-2.828 1.172-2.103-0.447-2.828-1.172-1.172-1.723-1.172-2.828 0.447-2.103 1.172-2.828 1.723-1.172 2.828-1.172 2.103 0.447 2.828 1.172 1.172 1.723 1.172 2.828zM11 1v2c0 0.552 0.448 1 1 1s1-0.448 1-1v-2c0-0.552-0.448-1-1-1s-1 0.448-1 1zM11 21v2c0 0.552 0.448 1 1 1s1-0.448 1-1v-2c0-0.552-0.448-1-1-1s-1 0.448-1 1zM3.513 4.927l1.42 1.42c0.391 0.391 1.024 0.391 1.414 0s0.391-1.024 0-1.414l-1.42-1.42c-0.391-0.391-1.024-0.391-1.414 0s-0.391 1.024 0 1.414zM17.653 19.067l1.42 1.42c0.391 0.391 1.024 0.391 1.414 0s0.391-1.024 0-1.414l-1.42-1.42c-0.391-0.391-1.024-0.391-1.414 0s-0.391 1.024 0 1.414zM1 13h2c0.552 0 1-0.448 1-1s-0.448-1-1-1h-2c-0.552 0-1 0.448-1 1s0.448 1 1 1zM21 13h2c0.552 0 1-0.448 1-1s-0.448-1-1-1h-2c-0.552 0-1 0.448-1 1s0.448 1 1 1zM4.927 20.487l1.42-1.42c0.391-0.391 0.391-1.024 0-1.414s-1.024-0.391-1.414 0l-1.42 1.42c-0.391 0.391-0.391 1.024 0 1.414s1.024 0.391 1.414 0zM19.067 6.347l1.42-1.42c0.391-0.391 0.391-1.024 0-1.414s-1.024-0.391-1.414 0l-1.42 1.42c-0.391 0.391-0.391 1.024 0 1.414s1.024 0.391 1.414 0z", - ), - ], - [], - ), - ]) -} - -pub fn icon_toggle_left() -> Html { - h("svg", [#("id", "icon-toggle-left"), #("viewBox", "0 0 24 24")], [ - h( - "path", - [ - #( - "d", - "M8 4c-2.209 0-4.21 0.897-5.657 2.343s-2.343 3.448-2.343 5.657 0.897 4.21 2.343 5.657 3.448 2.343 5.657 2.343h8c2.209 0 4.21-0.897 5.657-2.343s2.343-3.448 2.343-5.657-0.897-4.21-2.343-5.657-3.448-2.343-5.657-2.343zM8 6h8c1.657 0 3.156 0.67 4.243 1.757s1.757 2.586 1.757 4.243-0.67 3.156-1.757 4.243-2.586 1.757-4.243 1.757h-8c-1.657 0-3.156-0.67-4.243-1.757s-1.757-2.586-1.757-4.243 0.67-3.156 1.757-4.243 2.586-1.757 4.243-1.757zM12 12c0-1.104-0.449-2.106-1.172-2.828s-1.724-1.172-2.828-1.172-2.106 0.449-2.828 1.172-1.172 1.724-1.172 2.828 0.449 2.106 1.172 2.828 1.724 1.172 2.828 1.172 2.106-0.449 2.828-1.172 1.172-1.724 1.172-2.828zM10 12c0 0.553-0.223 1.051-0.586 1.414s-0.861 0.586-1.414 0.586-1.051-0.223-1.414-0.586-0.586-0.861-0.586-1.414 0.223-1.051 0.586-1.414 0.861-0.586 1.414-0.586 1.051 0.223 1.414 0.586 0.586 0.861 0.586 1.414z", - ), - ], - [], - ), - ]) -} - -pub fn icon_toggle_right() -> Html { - h("svg", [#("id", "icon-toggle-right"), #("viewBox", "0 0 24 24")], [ - h( - "path", - [ - #( - "d", - "M8 4c-2.209 0-4.21 0.897-5.657 2.343s-2.343 3.448-2.343 5.657 0.897 4.21 2.343 5.657 3.448 2.343 5.657 2.343h8c2.209 0 4.21-0.897 5.657-2.343s2.343-3.448 2.343-5.657-0.897-4.21-2.343-5.657-3.448-2.343-5.657-2.343zM8 6h8c1.657 0 3.156 0.67 4.243 1.757s1.757 2.586 1.757 4.243-0.67 3.156-1.757 4.243-2.586 1.757-4.243 1.757h-8c-1.657 0-3.156-0.67-4.243-1.757s-1.757-2.586-1.757-4.243 0.67-3.156 1.757-4.243 2.586-1.757 4.243-1.757zM20 12c0-1.104-0.449-2.106-1.172-2.828s-1.724-1.172-2.828-1.172-2.106 0.449-2.828 1.172-1.172 1.724-1.172 2.828 0.449 2.106 1.172 2.828 1.724 1.172 2.828 1.172 2.106-0.449 2.828-1.172 1.172-1.724 1.172-2.828zM18 12c0 0.553-0.223 1.051-0.586 1.414s-0.861 0.586-1.414 0.586-1.051-0.223-1.414-0.586-0.586-0.861-0.586-1.414 0.223-1.051 0.586-1.414 0.861-0.586 1.414-0.586 1.051 0.223 1.414 0.586 0.586 0.861 0.586 1.414z", - ), - ], - [], - ), - ]) -} diff --git a/src/tour.gleam b/src/tour.gleam index 57defdf..707b854 100644 --- a/src/tour.gleam +++ b/src/tour.gleam @@ -6,9 +6,9 @@ import gleam/result import gleam/string import gleam/string_builder import htmb.{h, text} -import icons import simplifile import snag +import tour/widgets const static = "static" @@ -245,6 +245,9 @@ fn write_content(chapters: List(Chapter)) -> snag.Result(Nil) { )), ) + // Everything page + use _ <- result.try(write_everything_page(chapters)) + Ok(Nil) } @@ -274,16 +277,28 @@ fn render_html(html: htmb.Html) -> String { |> string_builder.to_string } +fn ensure_directory(path: String) -> snag.Result(Nil) { + simplifile.create_directory_all(path) + |> file_error("Failed to create directory " <> path) +} + +fn write_text(path: String, text: String) -> snag.Result(Nil) { + simplifile.write(path, text) + |> file_error("Failed to write " <> path) +} + +fn write_everything_page(chapters: List(Chapter)) -> snag.Result(Nil) { + let path = public <> "/everything" + use _ <- result.try(ensure_directory(path)) + let path = filepath.join(path, "/index.html") + write_text(path, everything_html(chapters)) +} + fn write_lesson(lesson: Lesson) -> snag.Result(Nil) { let path = public <> lesson.path - use _ <- result.try( - simplifile.create_directory_all(path) - |> file_error("Failed to make " <> path), - ) - + use _ <- result.try(ensure_directory(path)) let path = filepath.join(path, "/index.html") - simplifile.write(to: path, contents: lesson_html(lesson)) - |> file_error("Failed to write page " <> path) + write_text(path, lesson_html(lesson)) } fn add_prev_next( @@ -500,6 +515,48 @@ fn file_error( } } +fn slugify_path(path: String) -> String { + string.replace(path, "/", "") +} + +fn everything_html(chapters: List(Chapter)) -> String { + let lessons = { + use chapter <- list.flat_map(chapters) + use lesson <- list.flat_map(chapter.lessons) + [ + h("h2", [#("id", slugify_path(lesson.path))], [text(lesson.name)]), + htmb.dangerous_unescaped_fragment(string_builder.from_string(lesson.text)), + h("pre", [], [h("code", [], [text(lesson.code)])]), + ] + } + + let table_of_contents = + list.map(chapters, fn(chapter) { + h("li", [], [ + h("h3", [], [text(chapter.name)]), + h( + "ul", + [], + list.map(chapter.lessons, fn(lesson) { + h("li", [], [ + h("a", [#("href", "#" <> slugify_path(lesson.path))], [ + text(lesson.name), + ]), + ]) + }), + ), + ]) + }) + + // TODO: use proper values for location and such + page_html(at: "everything", titled: "Everythingyyy!!!", containing: [ + h("main", [#("class", "everything")], [ + h("ul", [#("class", "everything-contents")], table_of_contents), + h("article", [#("class", "everything-lessons")], lessons), + ]), + ]) +} + fn lesson_html(page: Lesson) -> String { let navlink = fn(name, link) { case link { @@ -508,12 +565,44 @@ fn lesson_html(page: Lesson) -> String { } } + page_html(at: page.path, titled: page.name, containing: [ + h("article", [#("id", "playground")], [ + h("section", [#("id", "left")], [ + h("h2", [], [text(page.name)]), + htmb.dangerous_unescaped_fragment(string_builder.from_string(page.text)), + h("nav", [#("class", "prev-next")], [ + navlink("Back", page.previous), + text(" — "), + h("a", [#("href", page_contents)], [text("Contents")]), + text(" — "), + navlink("Next", page.next), + ]), + ]), + h("section", [#("id", "right")], [ + h("section", [#("id", "editor")], [ + h("div", [#("id", "editor-target")], []), + ]), + h("aside", [#("id", "output")], []), + ]), + ]), + h("script", [#("type", "gleam"), #("id", "code")], [ + htmb.dangerous_unescaped_fragment(string_builder.from_string(page.code)), + ]), + h("script", [#("type", "module"), #("src", "/index.js")], []), + ]) +} + +fn page_html( + at path: String, + titled title: String, + containing content: List(htmb.Html), +) -> String { let metaprop = fn(property, content) { h("meta", [#("property", property), #("content", content)], []) } let link = fn(rel, href) { h("link", [#("rel", rel), #("href", href)], []) } - let title = page.name <> " - The Gleam Language Tour" + let title = title <> " - The Gleam Language Tour" let description = "An interactive introduction and reference to the Gleam programming language. Learn Gleam in your browser!" @@ -533,10 +622,10 @@ fn lesson_html(page: Lesson) -> String { metaprop("og:type", "website"), metaprop("og:title", title), metaprop("og:description", description), - metaprop("og:url", "https://tour.gleam.run/" <> page.path), + metaprop("og:url", "https://tour.gleam.run/" <> path), metaprop("og:image", "https://gleam.run/images/og-image.png"), metaprop("twitter:card", "summary_large_image"), - metaprop("twitter:url", "https://tour.gleam.run/" <> page.path), + metaprop("twitter:url", "https://tour.gleam.run/" <> path), metaprop("twitter:title", title), metaprop("twitter:description", description), metaprop("twitter:image", "https://gleam.run/images/og-image.png"), @@ -554,7 +643,7 @@ fn lesson_html(page: Lesson) -> String { ), h("script", [#("type", "module")], [ htmb.dangerous_unescaped_fragment(string_builder.from_string( - theme_picker_js, + widgets.theme_picker_js, )), ]), ]), @@ -583,7 +672,7 @@ fn lesson_html(page: Lesson) -> String { #("class", "theme-button -light"), #("data-light-theme-toggle", ""), ], - [icons.icon_moon(), icons.icon_toggle_left()], + [widgets.icon_moon(), widgets.icon_toggle_left()], ), h( "button", @@ -594,84 +683,14 @@ fn lesson_html(page: Lesson) -> String { #("class", "theme-button -dark"), #("data-dark-theme-toggle", ""), ], - [icons.icon_sun(), icons.icon_toggle_right()], + [widgets.icon_sun(), widgets.icon_toggle_right()], ), ]), ]), ]), - h("article", [#("id", "playground")], [ - h("section", [#("id", "left")], [ - h("h2", [], [text(page.name)]), - htmb.dangerous_unescaped_fragment(string_builder.from_string( - page.text, - )), - h("nav", [#("class", "prev-next")], [ - navlink("Back", page.previous), - text(" — "), - h("a", [#("href", page_contents)], [text("Contents")]), - text(" — "), - navlink("Next", page.next), - ]), - ]), - h("section", [#("id", "right")], [ - h("section", [#("id", "editor")], [ - h("div", [#("id", "editor-target")], []), - ]), - h("aside", [#("id", "output")], []), - ]), - ]), - h("script", [#("type", "gleam"), #("id", "code")], [ - htmb.dangerous_unescaped_fragment(string_builder.from_string(page.code)), - ]), - h("script", [#("type", "module"), #("src", "/index.js")], []), + ..content ]), ]) |> htmb.render_page("html") |> string_builder.to_string } - -// This script is inlined in the response to avoid FOUC when applying the theme -const theme_picker_js = " -const mediaPrefersDarkTheme = window.matchMedia('(prefers-color-scheme: dark)') - -function selectTheme(selectedTheme) { - // Apply and remember the specified theme. - applyTheme(selectedTheme) - if ((selectedTheme === 'dark') === mediaPrefersDarkTheme.matches) { - // Selected theme is the same as the device's preferred theme, so we can forget this setting. - localStorage.removeItem('theme') - } else { - // Remember the selected theme to apply it on the next visit - localStorage.setItem('theme', selectedTheme) - } -} - -function applyTheme(theme) { - document.documentElement.classList.toggle('theme-dark', theme === 'dark') - document.documentElement.classList.toggle('theme-light', theme !== 'dark') -} - -// If user had selected a theme, load it. Otherwise, use device's preferred theme -const selectedTheme = localStorage.getItem('theme') -if (selectedTheme) { - applyTheme(selectedTheme) -} else { - applyTheme(mediaPrefersDarkTheme.matches ? 'dark' : 'light') -} - -// Watch the device's preferred theme and update theme if user did not select a theme -mediaPrefersDarkTheme.addEventListener('change', () => { - const selectedTheme = localStorage.getItem('theme') - if (!selectedTheme) { - applyTheme(mediaPrefersDarkTheme.matches ? 'dark' : 'light') - } -}) - -// Add handlers for theme selection buttons. -document.querySelector('[data-light-theme-toggle]').addEventListener('click', () => { - selectTheme('light') -}) -document.querySelector('[data-dark-theme-toggle]').addEventListener('click', () => { - selectTheme('dark') -}) -" diff --git a/src/tour/widgets.gleam b/src/tour/widgets.gleam new file mode 100644 index 0000000..0031cb7 --- /dev/null +++ b/src/tour/widgets.gleam @@ -0,0 +1,107 @@ +import htmb.{type Html, h} + +pub fn icon_moon() -> Html { + h("svg", [#("id", "icon-moon"), #("viewBox", "0 0 24 24")], [ + h( + "path", + [ + #( + "d", + "M21.996 12.882c0.022-0.233-0.038-0.476-0.188-0.681-0.325-0.446-0.951-0.544-1.397-0.219-0.95 0.693-2.060 1.086-3.188 1.162-1.368 0.092-2.765-0.283-3.95-1.158-1.333-0.985-2.139-2.415-2.367-3.935s0.124-3.124 1.109-4.456c0.142-0.191 0.216-0.435 0.191-0.691-0.053-0.55-0.542-0.952-1.092-0.898-2.258 0.22-4.314 1.18-5.895 2.651-1.736 1.615-2.902 3.847-3.137 6.386-0.254 2.749 0.631 5.343 2.266 7.311s4.022 3.313 6.772 3.567 5.343-0.631 7.311-2.266 3.313-4.022 3.567-6.772zM19.567 14.674c-0.49 1.363-1.335 2.543-2.416 3.441-1.576 1.309-3.648 2.016-5.848 1.813s-4.108-1.278-5.417-2.854-2.016-3.648-1.813-5.848c0.187-2.032 1.117-3.814 2.507-5.106 0.782-0.728 1.71-1.3 2.731-1.672-0.456 1.264-0.577 2.606-0.384 3.899 0.303 2.023 1.38 3.934 3.156 5.247 1.578 1.167 3.448 1.668 5.272 1.545 0.752-0.050 1.496-0.207 2.21-0.465z", + ), + ], + [], + ), + ]) +} + +pub fn icon_sun() -> Html { + h("svg", [#("id", "icon-sun"), #("viewBox", "0 0 24 24")], [ + h( + "path", + [ + #( + "d", + "M18 12c0-1.657-0.673-3.158-1.757-4.243s-2.586-1.757-4.243-1.757-3.158 0.673-4.243 1.757-1.757 2.586-1.757 4.243 0.673 3.158 1.757 4.243 2.586 1.757 4.243 1.757 3.158-0.673 4.243-1.757 1.757-2.586 1.757-4.243zM16 12c0 1.105-0.447 2.103-1.172 2.828s-1.723 1.172-2.828 1.172-2.103-0.447-2.828-1.172-1.172-1.723-1.172-2.828 0.447-2.103 1.172-2.828 1.723-1.172 2.828-1.172 2.103 0.447 2.828 1.172 1.172 1.723 1.172 2.828zM11 1v2c0 0.552 0.448 1 1 1s1-0.448 1-1v-2c0-0.552-0.448-1-1-1s-1 0.448-1 1zM11 21v2c0 0.552 0.448 1 1 1s1-0.448 1-1v-2c0-0.552-0.448-1-1-1s-1 0.448-1 1zM3.513 4.927l1.42 1.42c0.391 0.391 1.024 0.391 1.414 0s0.391-1.024 0-1.414l-1.42-1.42c-0.391-0.391-1.024-0.391-1.414 0s-0.391 1.024 0 1.414zM17.653 19.067l1.42 1.42c0.391 0.391 1.024 0.391 1.414 0s0.391-1.024 0-1.414l-1.42-1.42c-0.391-0.391-1.024-0.391-1.414 0s-0.391 1.024 0 1.414zM1 13h2c0.552 0 1-0.448 1-1s-0.448-1-1-1h-2c-0.552 0-1 0.448-1 1s0.448 1 1 1zM21 13h2c0.552 0 1-0.448 1-1s-0.448-1-1-1h-2c-0.552 0-1 0.448-1 1s0.448 1 1 1zM4.927 20.487l1.42-1.42c0.391-0.391 0.391-1.024 0-1.414s-1.024-0.391-1.414 0l-1.42 1.42c-0.391 0.391-0.391 1.024 0 1.414s1.024 0.391 1.414 0zM19.067 6.347l1.42-1.42c0.391-0.391 0.391-1.024 0-1.414s-1.024-0.391-1.414 0l-1.42 1.42c-0.391 0.391-0.391 1.024 0 1.414s1.024 0.391 1.414 0z", + ), + ], + [], + ), + ]) +} + +pub fn icon_toggle_left() -> Html { + h("svg", [#("id", "icon-toggle-left"), #("viewBox", "0 0 24 24")], [ + h( + "path", + [ + #( + "d", + "M8 4c-2.209 0-4.21 0.897-5.657 2.343s-2.343 3.448-2.343 5.657 0.897 4.21 2.343 5.657 3.448 2.343 5.657 2.343h8c2.209 0 4.21-0.897 5.657-2.343s2.343-3.448 2.343-5.657-0.897-4.21-2.343-5.657-3.448-2.343-5.657-2.343zM8 6h8c1.657 0 3.156 0.67 4.243 1.757s1.757 2.586 1.757 4.243-0.67 3.156-1.757 4.243-2.586 1.757-4.243 1.757h-8c-1.657 0-3.156-0.67-4.243-1.757s-1.757-2.586-1.757-4.243 0.67-3.156 1.757-4.243 2.586-1.757 4.243-1.757zM12 12c0-1.104-0.449-2.106-1.172-2.828s-1.724-1.172-2.828-1.172-2.106 0.449-2.828 1.172-1.172 1.724-1.172 2.828 0.449 2.106 1.172 2.828 1.724 1.172 2.828 1.172 2.106-0.449 2.828-1.172 1.172-1.724 1.172-2.828zM10 12c0 0.553-0.223 1.051-0.586 1.414s-0.861 0.586-1.414 0.586-1.051-0.223-1.414-0.586-0.586-0.861-0.586-1.414 0.223-1.051 0.586-1.414 0.861-0.586 1.414-0.586 1.051 0.223 1.414 0.586 0.586 0.861 0.586 1.414z", + ), + ], + [], + ), + ]) +} + +pub fn icon_toggle_right() -> Html { + h("svg", [#("id", "icon-toggle-right"), #("viewBox", "0 0 24 24")], [ + h( + "path", + [ + #( + "d", + "M8 4c-2.209 0-4.21 0.897-5.657 2.343s-2.343 3.448-2.343 5.657 0.897 4.21 2.343 5.657 3.448 2.343 5.657 2.343h8c2.209 0 4.21-0.897 5.657-2.343s2.343-3.448 2.343-5.657-0.897-4.21-2.343-5.657-3.448-2.343-5.657-2.343zM8 6h8c1.657 0 3.156 0.67 4.243 1.757s1.757 2.586 1.757 4.243-0.67 3.156-1.757 4.243-2.586 1.757-4.243 1.757h-8c-1.657 0-3.156-0.67-4.243-1.757s-1.757-2.586-1.757-4.243 0.67-3.156 1.757-4.243 2.586-1.757 4.243-1.757zM20 12c0-1.104-0.449-2.106-1.172-2.828s-1.724-1.172-2.828-1.172-2.106 0.449-2.828 1.172-1.172 1.724-1.172 2.828 0.449 2.106 1.172 2.828 1.724 1.172 2.828 1.172 2.106-0.449 2.828-1.172 1.172-1.724 1.172-2.828zM18 12c0 0.553-0.223 1.051-0.586 1.414s-0.861 0.586-1.414 0.586-1.051-0.223-1.414-0.586-0.586-0.861-0.586-1.414 0.223-1.051 0.586-1.414 0.861-0.586 1.414-0.586 1.051 0.223 1.414 0.586 0.586 0.861 0.586 1.414z", + ), + ], + [], + ), + ]) +} + +// This script is inlined in the response to avoid FOUC when applying the theme +pub const theme_picker_js = " +const mediaPrefersDarkTheme = window.matchMedia('(prefers-color-scheme: dark)') + +function selectTheme(selectedTheme) { + // Apply and remember the specified theme. + applyTheme(selectedTheme) + if ((selectedTheme === 'dark') === mediaPrefersDarkTheme.matches) { + // Selected theme is the same as the device's preferred theme, so we can forget this setting. + localStorage.removeItem('theme') + } else { + // Remember the selected theme to apply it on the next visit + localStorage.setItem('theme', selectedTheme) + } +} + +function applyTheme(theme) { + document.documentElement.classList.toggle('theme-dark', theme === 'dark') + document.documentElement.classList.toggle('theme-light', theme !== 'dark') +} + +// If user had selected a theme, load it. Otherwise, use device's preferred theme +const selectedTheme = localStorage.getItem('theme') +if (selectedTheme) { + applyTheme(selectedTheme) +} else { + applyTheme(mediaPrefersDarkTheme.matches ? 'dark' : 'light') +} + +// Watch the device's preferred theme and update theme if user did not select a theme +mediaPrefersDarkTheme.addEventListener('change', () => { + const selectedTheme = localStorage.getItem('theme') + if (!selectedTheme) { + applyTheme(mediaPrefersDarkTheme.matches ? 'dark' : 'light') + } +}) + +// Add handlers for theme selection buttons. +document.querySelector('[data-light-theme-toggle]').addEventListener('click', () => { + selectTheme('light') +}) +document.querySelector('[data-dark-theme-toggle]').addEventListener('click', () => { + selectTheme('dark') +}) +" -- cgit v1.2.3