I was going to say the same thing. I love seeing that even advanced CSS developers sometimes make mistakes. We're all human and it gives someone like me that little bit of hope. Like, hey maybe I can actually do this.
I rather have an hour long video where you dissect every step of your thought process (mistakes included), than have a 10 min video only showing how it's done and then I spend 4hours trying to figure out how to implement MY version of it :D
Nice one. A tiny note though. In your JS file `document.onload = setTheme()` this expression does not exactly do what you expect it to. When your page loads your JS file runs and when it comes to line 25 `setTheme()` function runs (retrieves data from localStorage, checks one of the radio buttons, etc...) and assigns `undefined` to the `document.onload`. Hence `onload` event doesn't really run at all. Moreover, for this type of action, you could probably use `DomContentLoaded` event. The DOMContentLoaded event fires when the HTML document has been completely parsed, and all deferred scripts ( and ) have downloaded and executed. It doesn't wait for other things like images, subframes, and async scripts to finish loading.
Also, I would place the event handler (just one) on the form and use event delegation instead of one event on each button. This is because having too many event handlers is very costly and it will eat your battery like nothing else. People on phone will hate you if you do not do event handling correctly.
I tried the codepen link given by Kevin and in firefox which doesn't support the has css tag, it doesn't work. So like you said, the JS portion doesn't do what is expected. I however don't understand what to modify to use DOMContentLoaded from your remark. Do you have an example?
@@SvenThielen It won't work in FF because CSS declarations that define theme styles are invalidated due to the fact FF doesn't support `:has`. That is, // When parser is trying to parse this declaration it invalidates it because it has an invalid (for the FF parser) selector (i.e. :has) .blue, :root:has(#blue:checked) {.... } If you want it to get to work in FF you first need to remove `:has` selectors (e.g. .pink {...} instead of .pink, :root:has(#blue:checked) {...}). Moreover, when you check radio buttons, there is no code in JS file which handles it. As for DOMContentLoaded what I meant was rewriting it in the following manner `document.addEventListener("DOMContentLoaded", setTheme)` Hope this helps.
@@AntiAtheismIsUnstoppable Optimising performance for 5 click handlers which are triggered on average once per visit is a wasted effort. The greatest "performance hit" in Kevins implementation if any is actually the document.querySelectorAll because it is a linear search trough the whole DOM. Would this concern you? I hope not. JavaScript is blazing fast these days. So no. Five click handlers will not "eat your users battery". What's more dangerous is mouse-move and scroll event handlers because they are triggered up to once per frame.
A better event than 'click' may be 'input', as _somebody_ is going to tab and enter on those radio buttons and bypass your event handler. I ran into that earlier this year in similar code.
Another improvement is Event Delegation. Instead of listening for the click event for each input, the event could be attached to the document, therefore, it's attached only once. Then you'd apply whatever you need in case an input was clicked.
@@RodrigoMendoza7 I agree on the concept of event delegation, but there is no one fit solution for everything and this type of general delegation seems pretty inefficient to me on the long run. On any average page these days there's much more happening than clicking on a radio button. Going "as far up" to the document wouldn't and shouldn't *always* be necessary. That form would do very well as the "delegate" and in case of "click" should probably also cancel the event from bubbling up, causing any other handlers to run for no good, possibly wasting time and memory. For *many* situations it's usually fine to just delegate the task to the next logical parent element ("component level") like the form in this case, and don't bother the rest of the code making logical decisions that could've been done with much earlier. If you attach all your same event type handlers only on the document (body) you'd eventually end up calling handlers that don't actually apply and test for the event target (DOM access) or some condition many times only to discard them most of the time.
I just love how Kevin always makes sure to include accessibility features to his website for visually impaired persons. In general, it's very rare in tutorials where you see things like that.
I used a very similar method when experimenting with themes before having ":has". The way I handled the styling flash is that I started with all color vars being black and setting a transition. So, the page starts totally black and fades to the selected color scheme.
IIRC, if the browser does not support a selector in a list of selectors (like .green, root:has()), it drops the entire rule. I think you can use :is() to fix that. Because that's not done in the video, the theme selector actually should not work when the browser doesn't have :has(), even with the presumable fallback.
Just saying: Instead of use forEach after retrieving the color for set the input radio, you can do document.querySelector(`input[type="radio"]#${activeTheme}]`).click(). 🍺
Just a JS note, the thing you assign to document.onLoad should be a function (reference). Your code has a slight problem on this line `document.onLoad = retrieveTheme()`; you’re invoking the retrieveTheme function and then setting `document.onLoad` to undefined (since that’s the return value of `retrieveTheme()`). The app works just fine since you’re invoking `retrieveTheme` regardless, but not in the way you probably intended. The correct version should just be `document.onLoad = retrieveTheme`. This way, you’re giving `onLoad` the function reference, and >IT< gets to decide when to invoke that function instead of you prematurely invoking it. Just thought to draw your attention to it. Fantastic work!! :)
You forgot to add that fallback code to the storeTheme function as well. Without Has support you'd need to refresh the page to see the theme change. Either way, always a big fan of your videos, good work.
What a fantastic tutorial! I learned so much and finally understood :has() a tiny bit better. However, when I'm trying it out in does work perfectly in Chrome but not at all in Firefox, even with the fallback :( I tried playing around with it to make it work in Firefox, but it just doesn't want to...
Fix the load flicker by just dropping a script tag in the HTML that fetches the theme and sets it to the document class name before loading the rest of the HTML elements. document.documentElement.className = localStorage.getItem("theme");
was hoping for some @media (prefers-color-scheme: dark/light) action. Can't figure out a good way to not have to repeat the defs within the media; one can use js to modify the css (doc.docEl.styles) directly and swap the definition for each light/dark color set (ie prefers dark becomes prefers light vice versa) which is valid except if you want to SSR a saved theme (cookie) because then you also need it available in a class to apply to :root (html) or body since even ff which is supposedly (mdn) supposed to obey parents color-scheme property doesn't seem to do so for :root/html so you can't just have :root.dark { color-scheme: dark; } as an override?
Maybe you don't need js at all. The link element supports a media attribute since ever, so you can create two separate stylesheets for each theme and let the browser decide upfront which one to download and apply. The other file download will be postponed and cached only used should the user actually switches theme. Having two individual files also simplifies keeping themin sync, wheres several @media in a single file are not only wasteful for users using the "other theme", is also makes things more complicated to keep all variables in sync. If you need JS for more conditions, the following article may also give you some ideas using the media attribute - filamentgroup.com/lab/load-css-simpler/ - based on their loadCSS project on Github. Enjoy.
yeah, and it does so since version 1!! And meanwhile they hid it in the classic menu bar (View -> Page Style) so many users won't be able to find it anyway... It's sad no other vendors picked this up. All you need is set a title attribute on your link element and off you go and pick that style from the Page Style submenu.
Really cool video. Just a question regarding the Javascript. Where you set the option to checked, why loop through them, since the element id is the same as the stored themename, you should be able to just do a "document.getElementById(activeTheme).checked = true" shouldn't you? That should remove an unnecessary loop, and a conditional. Not that it matter much, considering the small amount of items to loop through, but I'm always trying to see where I can skip loops or conditionals, and I think you can do that here.
@@metalstarver642 Ah, yeah. Very true. Still though, I'd rather use the conditional to check that than use a loop, but it's most likely just personal preference.
@@9990490677 I was thinking along those lines as well, but you'd need to get the selected radiobutton somehow, which would mean a loop through the forms children at each event instead of one loop at the initialization of the form. But then again, maybe we could get that radiobutton from the event.target. Not sure what element would be passed along here.
@@arkemiffo If one used the `value` attribute with the radio buttons, one could get the current selected theme using the FormData API. form.addEventListener('change', () => { storeTheme(new FormData(form).get('theme')) }) Then you could also `check` the stored theme like this: const theme = localStorage.getItem('theme') if (theme) { form.querySelector(`[value="${theme}"]`)?.checked = true } Note the `?` added to meet @MetalStarver's test case.
Hello! Thanks for posting this helpful video. I'm trying out web development after coming from Python, C#, and C++, and was wondering why you are defining functions by storing an anonymous function as a constant? Is this considered more idiomatic in javascript than function foo(bar) {} ?
Hello I really enjoyed the video and tested it out chrome and it seemed to work. I tried testing this on firefox but recently found out that :has() isn't compatible with firefox because of DOM issues and that doesn't seem like its going to be fixed soon. I believe the reason is that :has() doesn't work on html generated by javascript. Is there another way to do this without the :has()? With or without javascript?
11:28 Using the outline instead of the border creates here an accessibility issue, in that the focus ring is no longer visible on the element To get around this you could change the appearance of the outline during :focus-visible
It seems that 1. your "fallback" is *unconditional* vs a fallback, applying two solutions on browsers supporting :has(), and, 2. Please explain what the has-based solution buys you, even if fallback was conditional? (I expect a fallback to be a less optimal solution--in effect and/or performance--rather than provide identical results.
Thank you for this video. I have a question. This approach Is good, then I have predefined themes. But what if I want to add the ability for users to create their themes? What approach should I use in this case? Themes will be dynamic, created by users, and stored in a database. So I can't create a list of colors in a CSS file.
to solve the problem with jumping, you can remove the default checked property inside html, then add else {} block, and if(...) {...} else { colorThemes[0].checked = true }
Using cookies as opposed to localstorage could be a way around the window.onload issue. This would of course require the back-end renderer to do some of the job. But it doesn't have to be more than setting the initial html class or checking the correct select option.
You COULD have the inputs without any containers just floating between body and main and only apply the colors to the main element with input#pink:checked ~ main. As long as main is full screen I guess.
Great one! thiny thing (probably mentioned already) in your final fallback for no-has you set the html class to the variable theme but it should be activeTheme. Also, you need a default in casse there is nothing in localStorage, I guess.
Loved this video, I basically gave up on having multiple themes years ago. These days, I just do a dark and light theme and set it based on prefers-color-scheme media queries. This video gave me a new hope. I wish firefox gets a better ceo, I want them back working on improving servo, and implement new stuff under the hood, not the stupid colorway experiment and ui tweaks.. :has selector is amazing addition to css and my fav browser is lagging behind.
Is anyone else having a problem with the fallback? Everything works fine on Opera, but firefox doesnt seem to care about the fallback or anything else. Does anyone know a fix for that?
i believe you shouldnt name the storrage key theme since locastorrage doesnt differenciate between different websites, rather they all use one pool, so by naming it theme it can easily happen to be overwritten.. i usually call them unique_website_name-theme
How can we restore the current colours assuming these five new colours are additional and not the default? Going to assume we can delete all and any saved cookies related to theme. My knowledge of JS is very limited.
Hi Kevin, great video as always. I just have a question. Wouldn't it be better if you didn't set the first option as checked and only do it if there's no theme in the localstorage?? This way I think there wouldn't be that small jump on the load.
@@KevinPowell is it possible to run this specific JavaScript before the html and CSS? The page load would be a bit slower but no flicker. Just guessing. :)
@@narcos1024 You're right. I think to prevent the page from flickering, you'll have to write the JavaScript code for retrieving and setting the theme inside of the html document itself. This will block rendering of the html body until the theme has been set.
i coded theme switcher to my site and it didnt work on vs code but when i copied the whole thing to codepen it worked!! idk how.... whats the problem ??? Edit: i came to know that it doesnt work on firefox but works on chrome idk why
I am having a issue when I set my dark theme and refresh page it loads the light theme first for .5s because I have the styling with transitions , can anyone help I can’t figure it out
isn't ```document.getElementById(activeTheme).checked = true``` better than loop? ```document.getElementById(activeTheme)``` can be to a variable, of course, and check if the element exists before changing checked state
I don't want to be "that guy", but the 'theme' in the line 16 should be 'activeTheme', so the last run of the code worked because of the :has() pseudo-class 😅 Nice video tho, already want to try this approach to theming :)
Is there a way to place the theme switcher inside the navbar container and only change the "Content" area to black theme? I If I place the color switcher outside of the container I have the problem, that I cannot make the switcher responsive. Do you have an idea how to solve this problem? I would like to only use CSS and HTML, if possible.
For getting rid of that flicker of the checked state in the beginning, you could also set the body or the theme switcher's opacity to be 0, then set it to 1 (or add the appropriate show class to it) in setTheme(), along with a short opacity transition.
Or remove the `checked` attribute from the HTML. In the JS, you could set light to `checked` if no theme found in localStorage, but removing the hard-coded HTML attribute will remove the flicker.
Your CSS videos are always enjoyable to watch. Sadly, watching you write the JavaScript... That wasn't enjoyable. The entire code can be done in a much simpler way, more human readable and much more robust. There are better and worse ways of doing this, without using :has(), but I get the idea of the video.
Hi kevin, loved the tutorial. changing the root on checked input does not seen to work on firefox to me, only currently working on chrome. Have you heard of it?
Another way to store the theme selection would be through the use of cookies, but that's beyond the scope of HTML/CSS, it's more of a backend solution, but that will get rid of the flashing.
With flickering problem: You could probably solve it with few more lines of js. First, don't set default radio value. Call retrieveTheme. If there is theme saved in storage, set it, if not set default radio checked (light). Just a wild guess :) Nice :has solution.
Thanks for the video!!! Can you use the LAYERS panel in chrome inspection tool so we can visualize the changes in 3d so we can better understanda where everything is in relation to the screen. I thing it would look awesome if it appear when you are adding or modifying the css we have another perspective to how is implemented on the page. You can probably just put the LAYERS on another screen and record that screen separately.
you don't really need a cutting edge super machine to do web development, that's the great thing about it. Even a +10yr old used box would do. Find a used one, install a free and open lightweight Linux distribution like "Lubuntu" and you're good to go. That's how I once extended the lifetime of my very first laptop for another 5 years. It wasn't superfast, but all worked. You also don't need a super heavy Code Editor to write HTML, CSS and JS. There are many free open source options available. Good luck!
Another great tutorial, thanks. As far as I can tell f the browser doesn't support :has() I am not sure that, with your code, clicking on the radio buttons will actually change the theme until the page is reloaded. This can of course be overcome by adding more JavaScript or am I missing something?
I'm thrilled that if I have an ID on something I don't have to add a class to style it, I just use the attribute selector so specificity is the same but no extra class if not needed! [id=pink]. Felt really clever 😂
You can actually leverage :checked status of radios with the variables and don’t use :has at all, it work for me on every browser, and I am using it even for every mobile menu, buttons that open some containers and so on without JS.
I used to make a similar picker to one of my webpages when I was beginner. The secret was I created the same webpage in different colours and linked them in the index.html using the if command. It was fun but watching this video I have to realise i wasted my time 😂
Another great tutorial! 👌🏼 To fix the flash, using 'DOMContentLoaded' event instead of 'load' or put the script as inline script after the color picker element.
If you put the JS after the form, you can set the --vars immediately/synchronously rather than in the onload, avoiding the 'FOUC'. Or if your server isnt static you can put it in a cookie, and respond with the right theme in the header
Don't apologize for the mistake, you fixing it is valuable content! We need more mistakes lol
Agree. Btw, there are more mistakes to be found)
All I know and all I have is reached on mistakes :)
I was going to say the same thing. I love seeing that even advanced CSS developers sometimes make mistakes. We're all human and it gives someone like me that little bit of hope. Like, hey maybe I can actually do this.
Indeed Cryptologic. Mistakes=life and life=mistakes. I even love it that Kevin admitted it right away
I rather have an hour long video where you dissect every step of your thought process (mistakes included), than have a 10 min video only showing how it's done and then I spend 4hours trying to figure out how to implement MY version of it :D
Nice one. A tiny note though. In your JS file `document.onload = setTheme()`
this expression does not exactly do what you expect it to.
When your page loads your JS file runs and when it comes to line 25
`setTheme()` function runs (retrieves data from localStorage, checks one of the radio buttons, etc...)
and assigns `undefined` to the `document.onload`.
Hence `onload` event doesn't really run at all.
Moreover, for this type of action, you could probably use `DomContentLoaded` event.
The DOMContentLoaded event fires when the HTML document has been completely parsed, and all deferred scripts ( and ) have downloaded and executed. It doesn't wait for other things like images, subframes, and async scripts to finish loading.
Very well put. Thanks!
Also, I would place the event handler (just one) on the form and use event delegation instead of one event on each button. This is because having too many event handlers is very costly and it will eat your battery like nothing else. People on phone will hate you if you do not do event handling correctly.
I tried the codepen link given by Kevin and in firefox which doesn't support the has css tag, it doesn't work. So like you said, the JS portion doesn't do what is expected.
I however don't understand what to modify to use DOMContentLoaded from your remark. Do you have an example?
@@SvenThielen It won't work in FF because CSS declarations that define theme styles are invalidated due to the fact FF doesn't support `:has`.
That is,
// When parser is trying to parse this declaration it invalidates it because it has an invalid (for the FF parser) selector (i.e. :has)
.blue,
:root:has(#blue:checked) {.... }
If you want it to get to work in FF you first need to remove `:has` selectors (e.g. .pink {...} instead of .pink, :root:has(#blue:checked) {...}). Moreover, when you check radio buttons, there is no code in JS file which handles it.
As for DOMContentLoaded what I meant was rewriting it in the following manner
`document.addEventListener("DOMContentLoaded", setTheme)`
Hope this helps.
@@AntiAtheismIsUnstoppable Optimising performance for 5 click handlers which are triggered on average once per visit is a wasted effort.
The greatest "performance hit" in Kevins implementation if any is actually the document.querySelectorAll because it is a linear search trough the whole DOM. Would this concern you? I hope not. JavaScript is blazing fast these days.
So no. Five click handlers will not "eat your users battery".
What's more dangerous is mouse-move and scroll event handlers because they are triggered up to once per frame.
A better event than 'click' may be 'input', as _somebody_ is going to tab and enter on those radio buttons and bypass your event handler. I ran into that earlier this year in similar code.
Another improvement is Event Delegation. Instead of listening for the click event for each input, the event could be attached to the document, therefore, it's attached only once. Then you'd apply whatever you need in case an input was clicked.
@@RodrigoMendoza7 I agree on the concept of event delegation, but there is no one fit solution for everything and this type of general delegation seems pretty inefficient to me on the long run.
On any average page these days there's much more happening than clicking on a radio button.
Going "as far up" to the document wouldn't and shouldn't *always* be necessary. That form would do very well as the "delegate" and in case of "click" should probably also cancel the event from bubbling up, causing any other handlers to run for no good, possibly wasting time and memory.
For *many* situations it's usually fine to just delegate the task to the next logical parent element ("component level") like the form in this case, and don't bother the rest of the code making logical decisions that could've been done with much earlier.
If you attach all your same event type handlers only on the document (body) you'd eventually end up calling handlers that don't actually apply and test for the event target (DOM access) or some condition many times only to discard them most of the time.
I just love how Kevin always makes sure to include accessibility features to his website for visually impaired persons. In general, it's very rare in tutorials where you see things like that.
I used a very similar method when experimenting with themes before having ":has". The way I handled the styling flash is that I started with all color vars being black and setting a transition. So, the page starts totally black and fades to the selected color scheme.
smart!
Where did you put the transition "stuff"? I'm guessing it was in the body part of the CSS to ensure it was not duplicated in each specific theme.
I like how the mistake are fixed raw like without editing or trimming the video or even adding annotations. Thanks for that, Kevin
IIRC, if the browser does not support a selector in a list of selectors (like .green, root:has()), it drops the entire rule. I think you can use :is() to fix that. Because that's not done in the video, the theme selector actually should not work when the browser doesn't have :has(), even with the presumable fallback.
Would be cool to also incorporate prefers-color-scheme since a lot of devices (phones in particular) now allow the user to specify their preference
Just saying: Instead of use forEach after retrieving the color for set the input radio, you can do document.querySelector(`input[type="radio"]#${activeTheme}]`).click(). 🍺
Just a JS note, the thing you assign to document.onLoad should be a function (reference). Your code has a slight problem on this line `document.onLoad = retrieveTheme()`; you’re invoking the retrieveTheme function and then setting `document.onLoad` to undefined (since that’s the return value of `retrieveTheme()`). The app works just fine since you’re invoking `retrieveTheme` regardless, but not in the way you probably intended. The correct version should just be `document.onLoad = retrieveTheme`. This way, you’re giving `onLoad` the function reference, and >IT< gets to decide when to invoke that function instead of you prematurely invoking it. Just thought to draw your attention to it. Fantastic work!! :)
it should be
document.onload = retrieveTheme;
or
document.onload = () => retrieveTheme();
You forgot to add that fallback code to the storeTheme function as well. Without Has support you'd need to refresh the page to see the theme change. Either way, always a big fan of your videos, good work.
What a fantastic tutorial! I learned so much and finally understood :has() a tiny bit better. However, when I'm trying it out in does work perfectly in Chrome but not at all in Firefox, even with the fallback :( I tried playing around with it to make it work in Firefox, but it just doesn't want to...
Here before Web Dev Simplified.
Fix the load flicker by just dropping a script tag in the HTML that fetches the theme and sets it to the document class name before loading the rest of the HTML elements.
document.documentElement.className = localStorage.getItem("theme");
crude but effective :)
However, you should add some default value if the item wasn't set before
localStorage.getItem("theme") || "dark";
was hoping for some @media (prefers-color-scheme: dark/light) action. Can't figure out a good way to not have to repeat the defs within the media; one can use js to modify the css (doc.docEl.styles) directly and swap the definition for each light/dark color set (ie prefers dark becomes prefers light vice versa) which is valid except if you want to SSR a saved theme (cookie) because then you also need it available in a class to apply to :root (html) or body since even ff which is supposedly (mdn) supposed to obey parents color-scheme property doesn't seem to do so for :root/html so you can't just have :root.dark { color-scheme: dark; } as an override?
Maybe you don't need js at all. The link element supports a media attribute since ever, so you can create two separate stylesheets for each theme and let the browser decide upfront which one to download and apply.
The other file download will be postponed and cached only used should the user actually switches theme.
Having two individual files also simplifies keeping themin sync, wheres several @media in a single file are not only wasteful for users using the "other theme", is also makes things more complicated to keep all variables in sync.
If you need JS for more conditions, the following article may also give you some ideas using the media attribute - filamentgroup.com/lab/load-css-simpler/ - based on their loadCSS project on Github.
Enjoy.
Hi Kevin. Why don’t you use just accent color and transform to make them bigger?
Firefox actually has theme/css switch baked in that even documented in the MDN. But alas, it didn't make it to the WICG standard.
yeah, and it does so since version 1!! And meanwhile they hid it in the classic menu bar (View -> Page Style) so many users won't be able to find it anyway...
It's sad no other vendors picked this up. All you need is set a title attribute on your link element and off you go and pick that style from the Page Style submenu.
Really cool video.
Just a question regarding the Javascript. Where you set the option to checked, why loop through them, since the element id is the same as the stored themename, you should be able to just do a "document.getElementById(activeTheme).checked = true" shouldn't you? That should remove an unnecessary loop, and a conditional.
Not that it matter much, considering the small amount of items to loop through, but I'm always trying to see where I can skip loops or conditionals, and I think you can do that here.
Need to make sure element exists, to prevent exception in case in future IDs are changed or someone messes up with localstorage.
@@metalstarver642 Ah, yeah. Very true. Still though, I'd rather use the conditional to check that than use a loop, but it's most likely just personal preference.
To add to your point, we can use event delegation and listen to one form listener instead of managing individual listeners.
@@9990490677 I was thinking along those lines as well, but you'd need to get the selected radiobutton somehow, which would mean a loop through the forms children at each event instead of one loop at the initialization of the form.
But then again, maybe we could get that radiobutton from the event.target. Not sure what element would be passed along here.
@@arkemiffo If one used the `value` attribute with the radio buttons, one could get the current selected theme using the FormData API.
form.addEventListener('change', () => {
storeTheme(new FormData(form).get('theme'))
})
Then you could also `check` the stored theme like this:
const theme = localStorage.getItem('theme')
if (theme) {
form.querySelector(`[value="${theme}"]`)?.checked = true
}
Note the `?` added to meet @MetalStarver's test case.
beautiful stuff! thank you kevin!💪
one thing: on line 16 of the js you should have '= activeTheme' not '= theme'.
+1!
Hello! Thanks for posting this helpful video. I'm trying out web development after coming from Python, C#, and C++, and was wondering why you are defining functions by storing an anonymous function as a constant? Is this considered more idiomatic in javascript than function foo(bar) {} ?
Hello I really enjoyed the video and tested it out chrome and it seemed to work.
I tried testing this on firefox but recently found out that :has() isn't compatible with firefox because of DOM issues and that doesn't seem like its going to be fixed soon. I believe the reason is that :has() doesn't work on html generated by javascript.
Is there another way to do this without the :has()? With or without javascript?
When using js you can get rid of the flickering by using a script tag in the head. Vite does it on their docs site, it´s pretty easy :)
11:28 Using the outline instead of the border creates here an accessibility issue, in that the focus ring is no longer visible on the element
To get around this you could change the appearance of the outline during :focus-visible
Thank you!
It seems that
1. your "fallback" is *unconditional* vs a fallback, applying two solutions on browsers supporting :has(), and,
2. Please explain what the has-based solution buys you, even if fallback was conditional? (I expect a fallback to be a less optimal solution--in effect and/or performance--rather than provide identical results.
Thank you for this video. I have a question. This approach Is good, then I have predefined themes. But what if I want to add the ability for users to create their themes? What approach should I use in this case? Themes will be dynamic, created by users, and stored in a database. So I can't create a list of colors in a CSS file.
Why would you post a video title that is HTML and CSS, WHEN YOU ALSO USE JAVASCRIPT! I wanted to learn without the need of Javascript
to solve the problem with jumping, you can remove the default checked property inside html, then add else {} block, and if(...) {...} else { colorThemes[0].checked = true }
Using cookies as opposed to localstorage could be a way around the window.onload issue.
This would of course require the back-end renderer to do some of the job.
But it doesn't have to be more than setting the initial html class or checking the correct select option.
You COULD have the inputs without any containers just floating between body and main and only apply the colors to the main element with input#pink:checked ~ main. As long as main is full screen I guess.
main.js was not getting detected. Even after adding ' ' to the head of index.html
Great one! thiny thing (probably mentioned already) in your final fallback for no-has you set the html class to the variable theme but it should be activeTheme. Also, you need a default in casse there is nothing in localStorage, I guess.
Loved this video, I basically gave up on having multiple themes years ago. These days, I just do a dark and light theme and set it based on prefers-color-scheme media queries. This video gave me a new hope.
I wish firefox gets a better ceo, I want them back working on improving servo, and implement new stuff under the hood, not the stupid colorway experiment and ui tweaks.. :has selector is amazing addition to css and my fav browser is lagging behind.
is it possible to change the background of the html just using css3 without using js?
Thank you very much for your awesome video
Is anyone else having a problem with the fallback? Everything works fine on Opera, but firefox doesnt seem to care about the fallback or anything else. Does anyone know a fix for that?
There is a bug in your fallback JS. theme should be activeTheme
Not sure why it is not causing an error, but it should have.
Accessibility? Why would a blind person care about a color scheme UI?
not working at all, keep getting 'name="theme"' is not a valid selector, both firefox and chrome display same error
Have you checked browser compatibility :has()
Thanks
thankyou for appearance:none. I learns many things from your videos
Instead of apologizing for making a mistake, please consider editing your video.
i believe you shouldnt name the storrage key theme since locastorrage doesnt differenciate between different websites, rather they all use one pool, so by naming it theme it can easily happen to be overwritten.. i usually call them unique_website_name-theme
Thank you for demystifying this topic! I have implemented theme systems before, but they were WAY over engineered. 😃
My guy out here tryin' to keep the _color theme picker_ accessible for _blind_ people...
How can we restore the current colours assuming these five new colours are additional and not the default? Going to assume we can delete all and any saved cookies related to theme. My knowledge of JS is very limited.
Hi Kevin, great video as always. I just have a question. Wouldn't it be better if you didn't set the first option as checked and only do it if there's no theme in the localstorage?? This way I think there wouldn't be that small jump on the load.
I think it would still flicker, since we need the page to load and for it to pick one of them to be checked anyway. Worth testing out tho
@@KevinPowell is it possible to run this specific JavaScript before the html and CSS? The page load would be a bit slower but no flicker. Just guessing. :)
@@narcos1024 You're right. I think to prevent the page from flickering, you'll have to write the JavaScript code for retrieving and setting the theme inside of the html document itself. This will block rendering of the html body until the theme has been set.
You are amazing Kevin, please keep up the good work. Thanks
i coded theme switcher to my site and it didnt work on vs code but when i copied the whole thing to codepen it worked!!
idk how.... whats the problem ???
Edit: i came to know that it doesnt work on firefox but works on chrome idk why
The problem is that the page flashes until Javascript makes the selected theme selectable ;-), it's not usable
(especially on a larger site )
I am having a issue when I set my dark theme and refresh page it loads the light theme first for .5s because I have the styling with transitions , can anyone help I can’t figure it out
This is fantastic Kevin! 🎉 The :has property is amazing. I will teach this to my hispanic audience 😊
does :has work perfectly fine with container queries?
Quick honest question: does it even make sense to have a theme selector for users on a screen reader? Shouldn’t we just hide it altogether?
Is it possible to use this kind of button to make language switchers?
Oh, Lord Algorithm has popped this into my paws right on time...
Good work but teach us how to remove that first annoying color shift.
isn't ```document.getElementById(activeTheme).checked = true``` better than loop? ```document.getElementById(activeTheme)``` can be to a variable, of course, and check if the element exists before changing checked state
What does " A medium-legth title" mean? I ask for a friend
So many things i had no idea existed. Thank you for this
Kevin, thank you. It's mad how easy everything is when you explain it.
Nice explanation sir something new........
It is really valuable content, but could you please talk less. 🙏
Too bad one can't easily switch stylesheets or palettes.
main.js:8: -> `if(activeTheme) {document.getElementById(activeTheme).checked = True}`
^ 3 lines of code, Full function (no indentation):
const useTheme = localStorage.getItem('theme') ?? '';
const activeTheme = document.getElementById(activeTheme);
activeTheme.checked = true;
I don't want to be "that guy", but the 'theme' in the line 16 should be 'activeTheme', so the last run of the code worked because of the :has() pseudo-class 😅
Nice video tho, already want to try this approach to theming :)
don't worry :) Kevin did notice and added an errata in the video description. The correct code is in the codepen for this video.
Is there a way to place the theme switcher inside the navbar container and only change the "Content" area to black theme? I
If I place the color switcher outside of the container I have the problem, that I cannot make the switcher responsive. Do you have an idea how to solve this problem? I would like to only use CSS and HTML, if possible.
Oh okay I found the solution, I could use root:has as you showed in the video. Thanks a lot, couldn't figure out this for days
Awesome! Thank you for share this!!
Is there a way to get it working with Firefox? Please any suggestions. The fallback from the JS doesn't work.
check the updated Codepen example, link is in the video description as always
my has didn't work :( doesn't seem to recognise the theme id
For getting rid of that flicker of the checked state in the beginning, you could also set the body or the theme switcher's opacity to be 0, then set it to 1 (or add the appropriate show class to it) in setTheme(), along with a short opacity transition.
Or remove the `checked` attribute from the HTML. In the JS, you could set light to `checked` if no theme found in localStorage, but removing the hard-coded HTML attribute will remove the flicker.
@@toxicsnails Even better! 👍
thanks for sharing this knowledge
Your CSS videos are always enjoyable to watch.
Sadly, watching you write the JavaScript... That wasn't enjoyable.
The entire code can be done in a much simpler way, more human readable and much more robust.
There are better and worse ways of doing this, without using :has(), but I get the idea of the video.
Hi kevin, loved the tutorial.
changing the root on checked input does not seen to work on firefox to me, only currently working on chrome. Have you heard of it?
:has() isn't supported by Firefox yet
Great again Kevin! 😎 :has... I can't wait for all browsers to fully implement it.
Dude, you're awesome and spreading the wealth. WE APPRECIATE IT.
I never comment, like once in 5 years kinda never. You deserve the praise. Thank you.
Good luck Kevin. I referred your channel to my interns. You have wonderful contents. Big thumbs up for your effort 🤜🤛
Another way to store the theme selection would be through the use of cookies, but that's beyond the scope of HTML/CSS, it's more of a backend solution, but that will get rid of the flashing.
With flickering problem: You could probably solve it with few more lines of js.
First, don't set default radio value. Call retrieveTheme. If there is theme saved in storage, set it, if not set default radio checked (light). Just a wild guess :) Nice :has solution.
Thanks for the video!!!
Can you use the LAYERS panel in chrome inspection tool so we can visualize the changes in 3d so we can better understanda where everything is in relation to the screen.
I thing it would look awesome if it appear when you are adding or modifying the css we have another perspective to how is implemented on the page.
You can probably just put the LAYERS on another screen and record that screen separately.
I love web development but cannot afford a computer. What should I do?
you don't really need a cutting edge super machine to do web development, that's the great thing about it.
Even a +10yr old used box would do.
Find a used one, install a free and open lightweight Linux distribution like "Lubuntu" and you're good to go. That's how I once extended the lifetime of my very first laptop for another 5 years. It wasn't superfast, but all worked.
You also don't need a super heavy Code Editor to write HTML, CSS and JS. There are many free open source options available.
Good luck!
Another great tutorial, thanks.
As far as I can tell f the browser doesn't support :has() I am not sure that, with your code, clicking on the radio buttons will actually change the theme until the page is reloaded. This can of course be overcome by adding more JavaScript or am I missing something?
I'm thrilled that if I have an ID on something I don't have to add a class to style it, I just use the attribute selector so specificity is the same but no extra class if not needed! [id=pink]. Felt really clever 😂
wow... such a helpful content !
2:40 what extension for the `label+input` to HTML tag?
I think this is built-in functionality of the VS Code. I use WebStrom and in this IDE it works without additional plugins.
Thanks for the video!
You can actually leverage :checked status of radios with the variables and don’t use :has at all, it work for me on every browser, and I am using it even for every mobile menu, buttons that open some containers and so on without JS.
I used to make a similar picker to one of my webpages when I was beginner. The secret was I created the same webpage in different colours and linked them in the index.html using the if command. It was fun but watching this video I have to realise i wasted my time 😂
Amazing Tutorial.
I am gonna pee in my pants. Finally!
That was quite nice. I wanted to try it out, but the link to your code is missing...
Ooops! Just added it in! codepen.io/kevinpowell/pen/MWXybWJ
So humble man...How he is apologizing for one mistake!❤❤
Another great tutorial! 👌🏼
To fix the flash, using 'DOMContentLoaded' event instead of 'load' or put the script as inline script after the color picker element.
Love your vids. Just one thing, "The code from this video:" HAS() NOT() ;) link apparently. :P
Just added it, sorry about that! codepen.io/kevinpowell/pen/MWXybWJ
@@KevinPowell Great, thank you! :)
Thank you so much for sharing this great experience with us :) have a productive day
Thank you very much i would love to see you doing some more javascript concepts implemented with css!👍
:Has() is not even needed for this 😅
It's Jabba script with that shirt 😊
This is not working in Firefox
If you put the JS after the form, you can set the --vars immediately/synchronously rather than in the onload, avoiding the 'FOUC'. Or if your server isnt static you can put it in a cookie, and respond with the right theme in the header
wow ! you're just a Jedi