Added theme switcher

This commit is contained in:
Alexis
2025-06-21 21:33:25 +02:00
parent bfdd433a5f
commit 712d408736
12 changed files with 129 additions and 16 deletions

View File

@@ -1,7 +1,7 @@
// Animations on load
@media screen and (prefers-reduced-motion: no-preference) {
.appear-from-top {
animation: fadeIn .2s cubic-bezier(0.785, 0.135, 0.15, 0.86);
animation: fadeIn .2s var(--timing-function);
}
@keyframes fadeIn {

View File

@@ -11,7 +11,7 @@
cursor: pointer;
transition-property: color, background-color, border-color;
transition-duration: .2s;
transition-timing-function: cubic-bezier(0.785, 0.135, 0.15, 0.86);
transition-timing-function: var(--timing-function);
&:disabled {
cursor: not-allowed;
@@ -95,14 +95,14 @@
outline: .2rem solid transparent;
transition-property: color, background-color, border-color, outline-color;
transition-duration: .15s;
transition-timing-function: cubic-bezier(0.445, 0.05, 0.55, 0.95);
transition-timing-function: var(--timing-function);
svg {
fill: var(--foreground);
width: 1.15em;
transition-property: fill;
transition-duration: .15s;
transition-timing-function: cubic-bezier(0.445, 0.05, 0.55, 0.95);
transition-timing-function: var(--timing-function);
}
&:hover,

View File

@@ -19,7 +19,7 @@
border-radius: var(--border-radius);
transition-property: box-shadow;
transition-duration: .2s;
transition-timing-function: cubic-bezier(0.785, 0.135, 0.15, 0.86);
transition-timing-function: var(--timing-function);
&:focus-visible {
box-shadow: 0 0 0 .2rem color-mix(in srgb, var(--accent) 20%, transparent);

View File

@@ -39,8 +39,11 @@
box-shadow: none;
border-bottom-left-radius: 0;
border-bottom-right-radius: 0;
transition-property: color, text-shadow;
transition-duration: .15s;
transition-timing-function: var(--timing-function);
@include text.stroke(2, color-mix(in srgb, var(--foreground) 10%, var(--background)));
@include text.stroke(2, var(--background));
&::before {
display: none;
@@ -59,6 +62,9 @@
color: var(--foreground);
background-color: var(--background);
border-color: var(--border);
transition-property: color, background-color, border-color, outline-color;
transition-duration: .15s;
transition-timing-function: var(--timing-function);
&:hover,
&:focus-visible {
@@ -71,6 +77,9 @@
.leaflet-touch .leaflet-control-measure {
color: var(--foreground);
background-color: var(--background);
transition-property: color, background-color, border-color, outline-color;
transition-duration: .15s;
transition-timing-function: var(--timing-function);
.leaflet-control-measure-toggle {
color: var(--foreground);
@@ -78,6 +87,9 @@
border-color: var(--border);
background-image: url('/icon/ruler.svg');
background-size: 66%;
transition-property: color, background-color, border-color, outline-color;
transition-duration: .15s;
transition-timing-function: var(--timing-function);
&:hover,
&:focus-visible {

View File

@@ -2,7 +2,7 @@
background-color: color-mix(in srgb, var(--black) 50%, transparent);
position: fixed;
inset: 0;
animation: show-overlay 150ms cubic-bezier(0.16, 1, 0.3, 1);
animation: show-overlay 150ms var(--timing-function);
z-index: 100;
}
@@ -24,7 +24,7 @@
background-color: var(--background);
border: 1px solid var(--border);
box-shadow: var(--shadow);
animation: show-content 150ms cubic-bezier(0.16, 1, 0.3, 1);
animation: show-content 150ms var(--timing-function);
&:focus {
outline: none;

View File

@@ -51,20 +51,20 @@
}
}
.toast-root[data-state='open'] {
animation: slideIn 150ms cubic-bezier(0.16, 1, 0.3, 1);
animation: slideIn 150ms var(--timing-function);
}
.toast-root[data-state='closed'] {
animation: hide 100ms ease-in;
animation: hide 100ms var(--timing-function);
}
.toast-root[data-swipe='move'] {
transform: translateX(var(--radix-toast-swipe-move-x));
}
.toast-root[data-swipe='cancel'] {
transform: translateX(0);
transition: transform 200ms ease-out;
transition: transform 200ms var(--timing-function);
}
.toast-root[data-swipe='end'] {
animation: swipeOut 100ms ease-out;
animation: swipeOut 100ms var(--timing-function);
}
@keyframes hide {

View File

@@ -45,6 +45,7 @@
--border-radius: .25rem;
--shadow: color-mix(in srgb, var(--foreground) 10%, transparent) 0px 4px 12px;
--timing-function: cubic-bezier(0.785, 0.135, 0.15, 0.86);
}
.dark {

View File

@@ -0,0 +1,30 @@
<script lang="ts" setup>
import { useDark, useToggle } from '@vueuse/core'
const isDark = useDark({ storageKey: "theme", disableTransition: false })
const toggleDark = useToggle(isDark)
</script>
<template>
<div data-theme-toggle id="theme-switcher" class="wrapper appear-from-top">
<button @click="toggleDark()" class="btn-round">
<svg v-if="isDark" xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="#000000" viewBox="0 0 256 256"><path d="M120,40V32a8,8,0,0,1,16,0v8a8,8,0,0,1-16,0Zm72,88a64,64,0,1,1-64-64A64.07,64.07,0,0,1,192,128Zm-16,0a48,48,0,1,0-48,48A48.05,48.05,0,0,0,176,128ZM58.34,69.66A8,8,0,0,0,69.66,58.34l-8-8A8,8,0,0,0,50.34,61.66Zm0,116.68-8,8a8,8,0,0,0,11.32,11.32l8-8a8,8,0,0,0-11.32-11.32ZM192,72a8,8,0,0,0,5.66-2.34l8-8a8,8,0,0,0-11.32-11.32l-8,8A8,8,0,0,0,192,72Zm5.66,114.34a8,8,0,0,0-11.32,11.32l8,8a8,8,0,0,0,11.32-11.32ZM40,120H32a8,8,0,0,0,0,16h8a8,8,0,0,0,0-16Zm88,88a8,8,0,0,0-8,8v8a8,8,0,0,0,16,0v-8A8,8,0,0,0,128,208Zm96-88h-8a8,8,0,0,0,0,16h8a8,8,0,0,0,0-16Z"></path></svg>
<svg v-else xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="#000000" viewBox="0 0 256 256"><path d="M233.54,142.23a8,8,0,0,0-8-2,88.08,88.08,0,0,1-109.8-109.8,8,8,0,0,0-10-10,104.84,104.84,0,0,0-52.91,37A104,104,0,0,0,136,224a103.09,103.09,0,0,0,62.52-20.88,104.84,104.84,0,0,0,37-52.91A8,8,0,0,0,233.54,142.23ZM188.9,190.34A88,88,0,0,1,65.66,67.11a89,89,0,0,1,31.4-26A106,106,0,0,0,96,56,104.11,104.11,0,0,0,200,160a106,106,0,0,0,14.92-1.06A89,89,0,0,1,188.9,190.34Z"></path></svg>
</button>
</div>
</template>
<style lang="scss" scoped>
.wrapper {
pointer-events: all;
}
.btn-round {
position: relative;
> svg {
width: 1.25em;
height: 1.25em;
}
}
</style>

View File

@@ -8,6 +8,7 @@ import SearchMarkers from "./overlay/SearchMarkers.vue";
import LangSwitcher from "./overlay/LangSwitcher.vue";
import MapOverlayBreadcrumbs from "./MapOverlayBreadcrumbs.astro";
import ToastService from "./overlay/ToastService.vue";
import ThemeSwitch from "../global/ThemeSwitch.vue";
interface Props extends MapOverlayProps {}
@@ -41,9 +42,14 @@ const currentUrl = Astro.url
</SearchMarkers>
}
<div class="right-menu">
{ENABLE_I18N &&
<LangSwitcher currentUrl={currentUrl} client:only="vue" />
}
<ThemeSwitch client:only="vue" />
</div>
</div>
{ENABLE_BREADCRUMB &&
@@ -71,5 +77,15 @@ const currentUrl = Astro.url
align-items: top;
gap: 1rem;
}
.right-menu {
display: grid;
gap: .5rem;
align-content: flex-start;
@media screen and (width >= 900px) {
display: flex;
}
}
}
</style>

View File

@@ -54,6 +54,9 @@ menu {
border-radius: 100vmax;
box-shadow: rgba(0, 0, 0, 0.1) 0px 4px 12px;
pointer-events: all;
transition-property: color, background-color, border-color, outline-color;
transition-duration: .15s;
transition-timing-function: var(--timing-function);
li {
display: flex;

View File

@@ -314,6 +314,9 @@ function resetAllFields(actionAfter?: "focusAfter") {
border-radius: 25px;
box-shadow: var(--shadow);
pointer-events: all;
transition-property: color, background-color, border-color, outline-color;
transition-duration: .15s;
transition-timing-function: var(--timing-function);
@media screen and (width >= 900px) {
width: clamp(25rem, 35dvw, 30rem);

View File

@@ -58,7 +58,55 @@ const lang = getLangFromUrl(Astro.url) || defaultLang;
<link rel="stylesheet" href="/leaflet-measure/leaflet-measure.css" rel="preload" />
<script is:inline type="text/javascript" src="/js/leaflet-measure.min.js"></script>
</head>
<body class="dark">
<body>
<script is:inline>
let initialTheme = localStorage.getItem("theme");
const darkModeMq = window.matchMedia("(prefers-color-scheme: dark)");
// Initial paint from localstorage
switch (initialTheme) {
case "light":
enableLightTheme();
break;
case "system":
resetTheme();
break;
case "dark":
default:
enableDarkTheme();
break;
}
// Enables dark theme and updates preferences
function enableDarkTheme() {
document.documentElement.classList.add('dark');
document.documentElement.classList.remove('light');
localStorage.setItem("theme", "dark");
}
// Enables light theme and updates preferences
function enableLightTheme() {
document.documentElement.classList.add('light');
document.documentElement.classList.remove('dark');
localStorage.setItem("theme", "light");
}
// Reset theme to OS values
function resetTheme() {
const prefersDark = darkModeMq.matches;
if (prefersDark) {
document.documentElement.classList.add('dark');
document.documentElement.classList.remove('light');
} else {
document.documentElement.classList.add('light');
document.documentElement.classList.remove('dark');
}
localStorage.setItem("theme", "system");
}
</script>
<slot />
</body>
</html>