New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Switch Dark/Light mode using JS #1653
Conversation
This will allow you to load dark/light theme without reloading the page or depending on the link variables default theme is light and can be changed
Added class switchMode to switch theme
|
The latest updates on your projects. Learn more about Vercel for Git ↗︎
|
Neat! Does this also do "auto" (i.e. follow system preference)? Ideally a three-way toggle: light, dark, system. I'm currently hand-rolling an "auto" theme but it's a bit clunky 😅 |
selectedTheme = storedTheme ? storedTheme : defaultTheme; | ||
|
||
setTheme(selectedTheme) | ||
$(".switchMode").on('click', function (e) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is jQuery actually available here?
The demo-theme.js file is intentionally loaded at the top of the body so as to switch as fast as possible before the dom fully loads but the rest of the javascript is all loaded at the end so i wonder if this will work.
And even if it does work, i think using vanilla JS here to not have the overhead of jQuery loading in might be beneficial.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
By the time the user clicks, jQuery is likely loaded... so it works by accident.
But you are right, no reason to use jQuery for a simple click event.
const themeStorageKey = "tablerTheme" | ||
const defaultTheme = "light" | ||
let selectedTheme | ||
var themeStorageKey = "tablerTheme"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As the themeStorageKey
and defaultTheme
do not change during the execution of the script they where made const
what is the reason you are turning them into vars?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
var
is like $()
... time to let go 😁 (meaning: use let
or const
instead).
* | ||
* This will allow you to load dark/light theme wihtout reloading the page | ||
* and without depending on the link variables | ||
* default theme is light and can be default dark by changing the specified code below |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Your new comment is not a substitute for the original.
The reason the original comment is there is to explain why the script is loaded at the top of the body and not what it does. As this reason still applies please leave it intact.
|
||
// https://stackoverflow.com/a/901144 | ||
const params = new Proxy(new URLSearchParams(window.location.search), { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead of removing the functionality to switch theme with the url why not have both?
Have the button switch using javascript and leave the url variable intact to allow overriding what is currently set in local storage.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This would be unnecessary, having both of them
Not sure; but typically flickering can be avoided by performing the theme switch in the head or at least before body load. |
It was never javascript switching on the demo page, since implemented in #950 it had been url based switching
yup, I resolved the flickering issue in #1176 by loading the theme code in as the first thing in the body. |
By chance I saw this PR, and let me add I've done this theme switcher for Backpack. You can test it online; https://demo.backpackforlaravel.com/admin/dashboard If you guys want I can help with this one, let me know. |
I would love that! |
@promatik I just noticed though that the theme picker on that demo flashes (starts light, turns dark) when the system is in Dark mode. Think we could do one that doesn't flash? (see above) |
@promatik Hm, I'm not sure. I'm not really a front-end guy. I imagine you need to block painting of elements until some code runs. Maybe the script needs to be in I asked on Twitter, and got this reply: https://twitter.com/JakeDChampion/status/1683291322466070530 I still can't get the example to work, but in that thread you may find some helpful advice or ideas (or by viewing the page source). I dunno. Worth a look. |
@promatik Okay, the demo was updated and works for me now, without a flash: https://jakechampion.name/toggle.html Full code copied here, for reference: <html name=html id=html><style>
[data-theme="dark"] .sun-and-moon>.sun-beams {
opacity: 0
}
[data-theme="dark"] .sun-and-moon>.moon>circle {
transform: translate(-7px)
}
[data-theme="dark"] .laptop {
opacity: 0;
display: none;
}
[data-theme="light"] .laptop {
opacity: 0;
display: none;
}
[data-theme="system"] .laptop {
opacity: 1
}
[data-theme="system"] .sun-and-moon {
opacity: 0;
display: none;
}
.theme-toggle {
background: none;
border: none;
padding: 0;
border-radius: 50%;
outline-offset: 5px
}
[data-theme="dark"] .theme-toggle {
--icon-fill: hsl(210 10% 70%);
--icon-fill-hover: hsl(210 15% 90%)
}
html {
block-size: 100%;
color-scheme: light
}
html[data-theme="dark"] {
color-scheme: dark
}
@media (prefers-color-scheme:dark) {
html[data-theme="system"] {
color-scheme: dark;
}
.theme-toggle[data-theme="system"] {
color: hsl(210 10% 70%);
}
}
body {
display: grid;
align-content: center;
justify-content: center;
place-content: center
}
</style>
<script>
let storageKey = 't'
let pcsd = `(prefers-color-scheme:dark)`
let l = localStorage
let m = matchMedia
let getColorPreference = () => (l.getItem(storageKey) || (l.setItem(storageKey, 'system'), 'system'))
let reflectPreference = (v) => html.dataset.theme=v
let setPreference = (v) => l.setItem(storageKey, reflectPreference(toggle.ariaLabel=v))
// set early so no page flashes / CSS is made aware
reflectPreference(getColorPreference())
onload = () =>
(// set on load so screen readers can see latest value on the button
reflectPreference(getColorPreference()),
// now this script can find and listen for clicks on the control
// flip current value
toggle.onclick=() => {
let a = getColorPreference()
if (a == 'light') {
setPreference('system')
} else if (a == 'dark') {
setPreference('light')
} else {
setPreference('dark')
}
})
// sync with system changes
m(pcsd).onchange= ({ matches }) => reflectPreference(matches ? 'dark' : 'light')
</script>
<button class="theme-toggle" name=toggle id="toggle" title="Toggles light & dark" aria-label="auto" aria-live="polite">
<svg width="24" height="24" aria-hidden="true" class="sun-and-moon"><mask id="a" class="moon"><rect width="100%" height="100%" fill="#fff"/><circle cx="24" cy="10" r="6"/></mask><circle cx="12" cy="12" r="6" fill="currentColor" class="sun" mask="url(#a)"/><g stroke="currentColor" class="sun-beams"><path d="M12 1v2M12 21v2M4.22 4.22l1.42 1.42M18.36 18.36l1.42 1.42M1 12h2M21 12h2M4.22 19.78l1.42-1.42M18.36 5.64l1.42-1.42"/></g></svg>
<svg class=laptop width="24px" height="24px" ><path d="M4 6C4 4.89543 4.89543 4 6 4H18C19.1046 4 20 4.89543 20 6V14H4V6Z" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"/><path d="M20 14H4L2.44721 17.1056C1.78231 18.4354 2.7493 20 4.23607 20H19.7639C21.2507 20 22.2177 18.4354 21.5528 17.1056L20 14Z" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"/></svg>
</button>
</html> Huge thanks to Jake Champion for crafting this. See if that approach works! 💯 |
i can make it soon to work that way |
Can you please elaborate on flickering ? |
@YousefAlsbaihi I think "flash" might be a better word to describe it. if, for example, the default theme is light, the page loads light for a brief moment and then switches to dark if dark is what is configured. This creates a "flash" effect and it's quite irritating 🙃 |
Aw man :( |
This will allow users to switch light/dark mode without depending on link query and without reloading the page
This also uses browser storage same as before, just removed and added the js code to switch without reload.