Closed spences10 closed 3 years ago
Sure.
First install
npm i theme-change
Your .svelte
file would be like:
<button data-toggle-theme="dark,light" data-act-class="ACTIVECLASS">toggle theme</button>
<script>
import { onMount } from 'svelte';
import {themeChange} from "theme-change"
onMount(async () => {
themeChange(false)
})
// must use onMount because we don't want it on SSR.
</script>
And your global.css
file would be like:
:root {
--my-color: white;
background-color: var(--my-color);
}
[data-theme='dark'] {
--my-color: black ;
}
[data-theme='light'] {
--my-color: white;
}
Note that svelte will purge unused style from scoped CSS and styles like [data-theme='dark']
will be removed so we need to use it on a global CSS file.
Please let me know if you have any questions.
Nope! No questions, that's awesome! Thanks
So how do I switch between existing themes like loke,retro ?
@beebase data-toggle-theme="dark,light"
switches between dark
and light
so for example data-toggle-theme="valentine,retro"
will switch between valentine
and retro
Hi! How can I use this in svelte if I want to keep the Swap feature from daisyUI (https://daisyui.com/components/swap/)? I want to use it in this context:
` label class="swap swap-rotate class:swap-active={isDark}">
<svg class="swap-on fill-current w-6 h-6"[.....]"/>
<svg class="swap-off fill-current w-6 h-6 "[.......]"/> `
But if I do this, I need the isDark variable initialized, so where can I put this? Thanks!
@d3tr0id just did this myself. Copy and paste the sun/moon swap example code from your link and add data-toggle-theme
and data-act-class
to the checkbox input. That's it!
<input type="checkbox" data-toggle-theme="dark,light" data-act-class="ACTIVECLASS"/>
Yes I did that, but as soon I refresh the page, the wrong state is displayed from the swap icon. I have to handle the display of the correct icon by myself right? Still thanks for the response ! 👍
You're right - I overlooked that - refreshing the page resets the icon. I doubt the users will even notice as I didn't, as long as the theme and icon change when they press the button.
Okay, this works as a starting point:
The code in :else
has just inverted swap-on
and swap-off
Relevant code in __layout.svelte:
[EDIT]: this is the way - just added checked={theme === 'light'}
to checkbox input
<script>
import { browser } from '$app/env';
const theme = browser && localStorage.getItem('theme');
</script>
...
<label class="swap swap-rotate">
<input type="checkbox" data-toggle-theme="dark,light" data-act-class="ACTIVECLASS" checked={theme === 'light'} />
<svg class="swap-on fill-current w-5 h-5" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<path d="M5.64,17l-.71.71a1,1,0,0,0,0,1.41,1,1,0,0,0,1.41,0l.71-.71A1,1,0,0,0,5.64,17ZM5,12a1,1,0,0,0-1-1H3a1,1,0,0,0,0,2H4A1,1,0,0,0,5,12Zm7-7a1,1,0,0,0,1-1V3a1,1,0,0,0-2,0V4A1,1,0,0,0,12,5ZM5.64,7.05a1,1,0,0,0,.7.29,1,1,0,0,0,.71-.29,1,1,0,0,0,0-1.41l-.71-.71A1,1,0,0,0,4.93,6.34Zm12,.29a1,1,0,0,0,.7-.29l.71-.71a1,1,0,1,0-1.41-1.41L17,5.64a1,1,0,0,0,0,1.41A1,1,0,0,0,17.66,7.34ZM21,11H20a1,1,0,0,0,0,2h1a1,1,0,0,0,0-2Zm-9,8a1,1,0,0,0-1,1v1a1,1,0,0,0,2,0V20A1,1,0,0,0,12,19ZM18.36,17A1,1,0,0,0,17,18.36l.71.71a1,1,0,0,0,1.41,0,1,1,0,0,0,0-1.41ZM12,6.5A5.5,5.5,0,1,0,17.5,12,5.51,5.51,0,0,0,12,6.5Zm0,9A3.5,3.5,0,1,1,15.5,12,3.5,3.5,0,0,1,12,15.5Z" />
</svg>
<svg class="swap-off fill-current w-5 h-5" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<path d="M21.64,13a1,1,0,0,0-1.05-.14,8.05,8.05,0,0,1-3.37.73A8.15,8.15,0,0,1,9.08,5.49a8.59,8.59,0,0,1,.25-2A1,1,0,0,0,8,2.36,10.14,10.14,0,1,0,22,14.05,1,1,0,0,0,21.64,13Zm-9.5,6.69A8.14,8.14,0,0,1,7.08,5.22v.27A10.15,10.15,0,0,0,17.22,15.63a9.79,9.79,0,0,0,2.1-.22A8.11,8.11,0,0,1,12.14,19.73Z" />
</svg>
</label>
this works thanks! I was trying stuff out with observers and mutations to check on the data-theme attributesvalue change from the html element. Still trying to get it work, but your solution is much better!
But still im curious how to get this working with the swap-active
class that was made for this use case?
@d3tr0id I edited my comment but don't know if github notifies on edits. It's a better / more obvious solution.
Saw it. Works like a charm. Thanks for your time man really appreciate it!
Made me curious as well - here it is using swap-active
but it's more complex.
<script>
import { browser } from '$app/env';
let theme = browser && localStorage.getItem('theme');
function toggleTheme(e) {
if (theme === 'light')
theme = 'dark'
else
theme = 'light'
}
</script>
<label data-toggle-theme="dark,light" class="swap swap-rotate { theme === 'dark' ? 'swap-active' : '' }" on:click={toggleTheme}>
<svg class="swap-on fill-current w-5 h-5" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<path d="M5.64,17l-.71.71a1,1,0,0,0,0,1.41,1,1,0,0,0,1.41,0l.71-.71A1,1,0,0,0,5.64,17ZM5,12a1,1,0,0,0-1-1H3a1,1,0,0,0,0,2H4A1,1,0,0,0,5,12Zm7-7a1,1,0,0,0,1-1V3a1,1,0,0,0-2,0V4A1,1,0,0,0,12,5ZM5.64,7.05a1,1,0,0,0,.7.29,1,1,0,0,0,.71-.29,1,1,0,0,0,0-1.41l-.71-.71A1,1,0,0,0,4.93,6.34Zm12,.29a1,1,0,0,0,.7-.29l.71-.71a1,1,0,1,0-1.41-1.41L17,5.64a1,1,0,0,0,0,1.41A1,1,0,0,0,17.66,7.34ZM21,11H20a1,1,0,0,0,0,2h1a1,1,0,0,0,0-2Zm-9,8a1,1,0,0,0-1,1v1a1,1,0,0,0,2,0V20A1,1,0,0,0,12,19ZM18.36,17A1,1,0,0,0,17,18.36l.71.71a1,1,0,0,0,1.41,0,1,1,0,0,0,0-1.41ZM12,6.5A5.5,5.5,0,1,0,17.5,12,5.51,5.51,0,0,0,12,6.5Zm0,9A3.5,3.5,0,1,1,15.5,12,3.5,3.5,0,0,1,12,15.5Z" />
</svg>
<svg class="swap-off fill-current w-5 h-5" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<path d="M21.64,13a1,1,0,0,0-1.05-.14,8.05,8.05,0,0,1-3.37.73A8.15,8.15,0,0,1,9.08,5.49a8.59,8.59,0,0,1,.25-2A1,1,0,0,0,8,2.36,10.14,10.14,0,1,0,22,14.05,1,1,0,0,0,21.64,13Zm-9.5,6.69A8.14,8.14,0,0,1,7.08,5.22v.27A10.15,10.15,0,0,0,17.22,15.63a9.79,9.79,0,0,0,2.1-.22A8.11,8.11,0,0,1,12.14,19.73Z" />
</svg>
</label>
@myleftshoe, How would I make a component from this and import in __layout.svelte file?
@myleftshoe, How would I make a component from this and import in __layout.svelte file?
I made an example project that I use on the regular here: https://github.com/spences10/sveltekit-theme-switch-example
Thanks @spences10. I have two more questions.
That split second is because HTML and CSS load first then you see the page with default theme, then JS loads and reads the local storage and switches the theme.
If we find a way to load the JS before the page load, this will be fixed but I think it will result negative effect on page load speed. For example if you use the CDN file of theme-change and put it in the <head>
before every other file, you can see the difference.
Check out this youtube vid for a comprehensive solution including the initial flash.
How to: SvelteKit SSR Dark Mode Disclaimer: I haven't tried it myself
Here's a ThemeManagerComponent i made for own project - you still get the flash at the start.
It's a self contained component - everything you need is in that folder. Maybe it's more than you need - it also changes the color of the surrounding UI on iOS PWA to match the appbar color.
And this is the __layout.svelte that uses it.
Feel free to copy the code, it's public.
here's my solution using CSS only. It looks forhtml data-theme
attribute and changes opacities accordingly
Using DaisyUI ThemeSwitch.svelte
<label class="swap">
<input
type="checkbox"
data-toggle-theme="dark,winter"
data-act-class="ACTIVECLASS"
/>
<svg
class="swap-on fill-current w-10 h-10"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
>
<path
d="M5.64,17l-.71.71a1,1,0,0,0,0,1.41,1,1,0,0,0,1.41,0l.71-.71A1,1,0,0,0,5.64,17ZM5,12a1,1,0,0,0-1-1H3a1,1,0,0,0,0,2H4A1,1,0,0,0,5,12Zm7-7a1,1,0,0,0,1-1V3a1,1,0,0,0-2,0V4A1,1,0,0,0,12,5ZM5.64,7.05a1,1,0,0,0,.7.29,1,1,0,0,0,.71-.29,1,1,0,0,0,0-1.41l-.71-.71A1,1,0,0,0,4.93,6.34Zm12,.29a1,1,0,0,0,.7-.29l.71-.71a1,1,0,1,0-1.41-1.41L17,5.64a1,1,0,0,0,0,1.41A1,1,0,0,0,17.66,7.34ZM21,11H20a1,1,0,0,0,0,2h1a1,1,0,0,0,0-2Zm-9,8a1,1,0,0,0-1,1v1a1,1,0,0,0,2,0V20A1,1,0,0,0,12,19ZM18.36,17A1,1,0,0,0,17,18.36l.71.71a1,1,0,0,0,1.41,0,1,1,0,0,0,0-1.41ZM12,6.5A5.5,5.5,0,1,0,17.5,12,5.51,5.51,0,0,0,12,6.5Zm0,9A3.5,3.5,0,1,1,15.5,12,3.5,3.5,0,0,1,12,15.5Z"
/>
</svg>
<svg
class="swap-off fill-current w-10 h-10"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
>
<path
d="M21.64,13a1,1,0,0,0-1.05-.14,8.05,8.05,0,0,1-3.37.73A8.15,8.15,0,0,1,9.08,5.49a8.59,8.59,0,0,1,.25-2A1,1,0,0,0,8,2.36,10.14,10.14,0,1,0,22,14.05,1,1,0,0,0,21.64,13Zm-9.5,6.69A8.14,8.14,0,0,1,7.08,5.22v.27A10.15,10.15,0,0,0,17.22,15.63a9.79,9.79,0,0,0,2.1-.22A8.11,8.11,0,0,1,12.14,19.73Z"
/>
</svg>
</label>
<style>
:global(html[data-theme='dark']) label > svg:first-of-type {
opacity: 0;
}
:global(html[data-theme='dark']) label > svg:last-of-type {
opacity: 1;
}
:global(html[data-theme='winter']) svg:first-of-type {
opacity: 1;
}
:global(html[data-theme='winter']) svg:last-of-type {
opacity: 0;
}
</style>
For getting rid of the flash you can inject the following in app.html's <head>...</head>
:
<script>try{document.documentElement.setAttribute('data-theme',localStorage.getItem('theme'));}catch(e){}</script>
For getting rid of the flash you can inject the following in app.html's
<head>...</head>
:<script>try{document.documentElement.setAttribute('data-theme',localStorage.getItem('theme'));}catch(e){}</script>
THIS!
I wish I read this before going down the cookie route! 😅
Anyone else wanting to use a theme cookie check out the updated version of the spences10/sveltekit-theme-switch-example
Also see the blog post on how it's done: https://scottspence.com/posts/cookie-based-theme-selection-in-sveltekit-with-daisyui
@spences10 but doesn't this affect the lighthouse score because of blocking the HTML load? I haven't tried it myself but I thought it would block the HTML load.
@spences10 but doesn't this affect the lighthouse score because of blocking the HTML load? I haven't tried it myself but I thought it would block the HTML load.
Using the <script>
tag approach or cookie @saadeghi?
I haven't tried the scripty way so not tested it
@spences10 The inline script. I was afraid something like this would block the page load and lower lighthouse performance score but I can confirm that I tried it and everything's fine.
Hi, is there a SvelteKit example I could take a look at anywhere?
If the only way to do it is with HTML tags then that's cool 👍