Add dark mode

This commit is contained in:
Thelonius Kort
2023-02-04 23:10:57 +01:00
parent 4cb07692b1
commit ab2e8ae816
10 changed files with 102 additions and 4 deletions

View File

@ -22,8 +22,12 @@ import {Socket} from "phoenix"
import {LiveSocket} from "phoenix_live_view" import {LiveSocket} from "phoenix_live_view"
import topbar from "../vendor/topbar" import topbar from "../vendor/topbar"
import {DarkModeHook} from './dark-mode'
let csrfToken = document.querySelector("meta[name='csrf-token']").getAttribute("content") let csrfToken = document.querySelector("meta[name='csrf-token']").getAttribute("content")
let liveSocket = new LiveSocket("/live", Socket, {params: {_csrf_token: csrfToken}}) let liveSocket = new LiveSocket("/live", Socket, {
params: {_csrf_token: csrfToken},
hooks: {dark_mode_widget: DarkModeHook},
})
// Show progress bar on live navigation and form submits // Show progress bar on live navigation and form submits
topbar.config({barColors: {0: "#29d"}, shadowColor: "rgba(0, 0, 0, .3)"}) topbar.config({barColors: {0: "#29d"}, shadowColor: "rgba(0, 0, 0, .3)"})

36
assets/js/dark-mode.js Normal file
View File

@ -0,0 +1,36 @@
let DarkModeHook = {
mounted() {
this.button = this.el.querySelector("button")
this.button.addEventListener("click", this.show_selector.bind(this))
this.selector = this.el.querySelector("ul")
var lis = this.el.querySelectorAll("li")
lis[0].addEventListener("click", this.switch_to_day_mode.bind(this))
lis[1].addEventListener("click", this.switch_to_night_mode.bind(this))
lis[2].addEventListener("click", this.switch_to_system_mode.bind(this))
},
show_selector(){
this.selector.classList.remove("hidden")
},
switch_to_day_mode() {
document.documentElement.classList.remove('dark')
localStorage.theme = 'light'
this.selector.classList.add("hidden")
},
switch_to_night_mode() {
document.documentElement.classList.add('dark')
localStorage.theme = 'dark'
this.selector.classList.add("hidden")
},
switch_to_system_mode() {
if (window.matchMedia('(prefers-color-scheme: dark)').matches) {
document.documentElement.classList.add('dark')
} else {
document.documentElement.classList.remove('dark')
}
localStorage.removeItem('theme')
this.selector.classList.add("hidden")
},
}
export {DarkModeHook}

View File

@ -4,6 +4,7 @@
const plugin = require("tailwindcss/plugin") const plugin = require("tailwindcss/plugin")
module.exports = { module.exports = {
darkMode: 'class',
content: [ content: [
"./js/**/*.js", "./js/**/*.js",
"../lib/*_web.ex", "../lib/*_web.ex",
@ -23,4 +24,4 @@ module.exports = {
plugin(({addVariant}) => addVariant("phx-submit-loading", [".phx-submit-loading&", ".phx-submit-loading &"])), plugin(({addVariant}) => addVariant("phx-submit-loading", [".phx-submit-loading&", ".phx-submit-loading &"])),
plugin(({addVariant}) => addVariant("phx-change-loading", [".phx-change-loading&", ".phx-change-loading &"])) plugin(({addVariant}) => addVariant("phx-change-loading", [".phx-change-loading&", ".phx-change-loading &"]))
] ]
} }

View File

@ -90,6 +90,7 @@ defmodule OutlookWeb do
import OutlookWeb.HtmlDocComponent import OutlookWeb.HtmlDocComponent
import OutlookWeb.TunitEditorComponent import OutlookWeb.TunitEditorComponent
import OutlookWeb.PublicComponents import OutlookWeb.PublicComponents
import OutlookWeb.DarkModeComponent
import OutlookWeb.Gettext import OutlookWeb.Gettext
# Shortcut for generating JS commands # Shortcut for generating JS commands

View File

@ -0,0 +1,38 @@
defmodule OutlookWeb.DarkModeComponent do
@moduledoc """
Provides components for showing and listing artikel and autoren.
"""
use Phoenix.Component
import Phoenix.HTML
# alias Phoenix.LiveView.JS
def dark_mode_widget(assigns) do
~H"""
<div id="dark-mode-widget" class="relative flex flex-row-reverse p-4" phx-hook="dark_mode_widget">
<button type="button" id="headlessui-listbox-button-4" aria-haspopup="true" aria-expanded="true" data-headlessui-state="open" >
<span class="dark:hidden">
<Heroicons.sun class="w-5 h-5 stroke-slate-400 dark:stroke-slate-500" />
</span>
<span class="hidden dark:inline">
<Heroicons.moon class="w-5 h-5 stroke-slate-400 dark:stroke-slate-500" />
</span>
</button>
<ul class="hidden absolute z-50 top right-2 bg-white rounded-lg ring-1 ring-slate-900/10 shadow-lg overflow-hidden w-36 py-1 text-sm text-slate-700 font-semibold dark:bg-slate-800 dark:ring-0 dark:highlight-white/5 dark:text-slate-300 mt-8" aria-labelledby="headlessui-listbox-label-3" aria-orientation="vertical" id="headlessui-listbox-options-5" role="listbox" tabindex="0" data-headlessui-state="open">
<li class="py-1 px-2 flex items-center cursor-pointer" id="selector-option-1" role="option" tabindex="-1" aria-selected="false" data-headlessui-state="">
<Heroicons.sun class="w-5 h-5 mr-2 stroke-slate-400 dark:stroke-slate-500" />
Light
</li>
<li class="py-1 px-2 flex items-center cursor-pointer" id="selector-option-2" role="option" tabindex="-1" aria-selected="false" data-headlessui-state="">
<Heroicons.moon class="w-5 h-5 mr-2 stroke-slate-400 dark:stroke-slate-500" />
Dark
</li>
<li class="py-1 px-2 flex items-center cursor-pointer text-slate-700 dark:text-slate-300" id="selector-option-3" role="option" tabindex="-1" aria-selected="true" data-headlessui-state="selected">
<Heroicons.computer_desktop class="w-5 h-5 mr-2 stroke-slate-400 dark:stroke-slate-500" />
System
</li>
</ul>
</div>
"""
end
end

View File

@ -34,6 +34,7 @@
</a> </a>
</div> </div>
</div> </div>
<.dark_mode_widget />
</header> </header>
<main class="px-4 py-20 sm:px-6 lg:px-8"> <main class="px-4 py-20 sm:px-6 lg:px-8">
<div class="mx-auto max-w-4xl"> <div class="mx-auto max-w-4xl">

View File

@ -10,8 +10,16 @@
<link phx-track-static rel="stylesheet" href={~p"/assets/app.css"} /> <link phx-track-static rel="stylesheet" href={~p"/assets/app.css"} />
<script defer phx-track-static type="text/javascript" src={~p"/assets/app.js"}> <script defer phx-track-static type="text/javascript" src={~p"/assets/app.js"}>
</script> </script>
<script type="text/javascript">
// On page load or when changing themes, best to add inline in `head` to avoid FOUC
if (localStorage.theme === 'dark' || (!('theme' in localStorage) && window.matchMedia('(prefers-color-scheme: dark)').matches)) {
document.documentElement.classList.add('dark')
} else {
document.documentElement.classList.remove('dark')
}
</script>
</head> </head>
<body class="bg-white antialiased max-h-screen"> <body class="bg-white dark:bg-stone-900 dark:text-stone-100 antialiased max-h-screen">
<%= @inner_content %> <%= @inner_content %>
</body> </body>
</html> </html>

View File

@ -3,6 +3,7 @@
<img class="w-full" src="/images/elbefoto-lg.jpg" <img class="w-full" src="/images/elbefoto-lg.jpg"
src-set="elbefoto-xxl.jpg 4496w, /images/elbefoto-lg.jpg 2248w, /images/elbefoto-md.jpg 1199w, /images/elbefoto-sm.jpg 991w, /images/elbefoto-xs.jpg 767w"> src-set="elbefoto-xxl.jpg 4496w, /images/elbefoto-lg.jpg 2248w, /images/elbefoto-md.jpg 1199w, /images/elbefoto-sm.jpg 991w, /images/elbefoto-xs.jpg 767w">
</a> </a>
<.dark_mode_widget />
</header> </header>
<main class="px-4 py-20 sm:px-6 lg:px-8"> <main class="px-4 py-20 sm:px-6 lg:px-8">
<div class="mx-auto max-w-4xl"> <div class="mx-auto max-w-4xl">

View File

@ -10,6 +10,14 @@
<link phx-track-static rel="stylesheet" href={~p"/assets/app.css"} /> <link phx-track-static rel="stylesheet" href={~p"/assets/app.css"} />
<script defer phx-track-static type="text/javascript" src={~p"/assets/app.js"}> <script defer phx-track-static type="text/javascript" src={~p"/assets/app.js"}>
</script> </script>
<script type="text/javascript">
// On page load or when changing themes, best to add inline in `head` to avoid FOUC
if (localStorage.theme === 'dark' || (!('theme' in localStorage) && window.matchMedia('(prefers-color-scheme: dark)').matches)) {
document.documentElement.classList.add('dark')
} else {
document.documentElement.classList.remove('dark')
}
</script>
</head> </head>
<body class="bg-white antialiased max-h-screen"> <body class="bg-white antialiased max-h-screen">
<ul> <ul>

View File

@ -1,5 +1,5 @@
<header class="mb-20"> <header class="mb-20">
<h1 class="text-lg font-semibold leading-8 text-zinc-800"><%= @artikel.title %></h1> <h1 class="text-lg font-semibold leading-8 text-stone-800 dark:text-stone-200"><%= @artikel.title %></h1>
<p class="my-2"><.link href={"/autoren/#{@artikel.article.author.id}"}><%= @artikel.article.author.name %></.link> <p class="my-2"><.link href={"/autoren/#{@artikel.article.author.id}"}><%= @artikel.article.author.name %></.link>
&nbsp;&nbsp;&nbsp; — &nbsp;&nbsp;&nbsp;<%= Calendar.strftime(@artikel.article.date, "%d.%m.%Y") %></p> &nbsp;&nbsp;&nbsp; — &nbsp;&nbsp;&nbsp;<%= Calendar.strftime(@artikel.article.date, "%d.%m.%Y") %></p>
<div>Original <div>Original