{"id":1716,"date":"2024-04-18T17:43:30","date_gmt":"2024-04-18T23:43:30","guid":{"rendered":"https:\/\/frontendmasters.com\/blog\/?p=1716"},"modified":"2024-04-18T17:43:31","modified_gmt":"2024-04-18T23:43:31","slug":"dark-and-light","status":"publish","type":"post","link":"https:\/\/frontendmasters.com\/blog\/dark-and-light\/","title":{"rendered":"What&#8217;s Going On in Dark Theme \/ Light Theme Land"},"content":{"rendered":"\n<p>There has been a fresh round of enthusiasm and writing around light mode \/ dark mode support for the web lately. I think it&#8217;s driven partially by <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/CSS\/color_value\/light-dark\">the new <code>light-dark()<\/code> function<\/a> in CSS (<a href=\"https:\/\/drafts.csswg.org\/css-color-5\/#light-dark\">CSS Color Module Level 5 spec<\/a>) that makes it easier to declare values that change depending on the mode. Here&#8217;s the basic usage:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-1\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-tag\">html<\/span> {\n  <span class=\"hljs-attribute\">color-scheme<\/span>: light dark;\n\n  <span class=\"hljs-attribute\">background<\/span>: <span class=\"hljs-built_in\">light-dark<\/span>(white, black);\n  <span class=\"hljs-attribute\">color<\/span>: <span class=\"hljs-built_in\">light-dark<\/span>(black, white);\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-1\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>In real life, you&#8217;d probably be using custom properties with your colors. So you&#8217;d set up your colors for light mode, then when the special dark mode media query matches, you&#8217;d re-declare all those variables, and then you&#8217;d use them. <a href=\"https:\/\/pawelgrzybek.com\/\">Pawe\u0142 Grzybek<\/a> has a nice basic explanation. Here&#8217;s a comparison.<\/p>\n\n\n\n<div class=\"wp-block-columns is-layout-flex wp-container-core-columns-is-layout-9d6595d7 wp-block-columns-is-layout-flex\">\n<div class=\"wp-block-column is-layout-flow wp-block-column-is-layout-flow\">\n<p>You used to have to do this:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-2\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-pseudo\">:root<\/span> {\n  <span class=\"hljs-attribute\">color-scheme<\/span>: light dark;\n\n  <span class=\"hljs-attribute\">--dark-color<\/span>: <span class=\"hljs-number\">#292524<\/span>;\n  <span class=\"hljs-attribute\">--light-color<\/span>: <span class=\"hljs-number\">#f5f5f4<\/span>;\n\n  <span class=\"hljs-attribute\">--text-color<\/span>: <span class=\"hljs-built_in\">var<\/span>(--dark-color);\n  <span class=\"hljs-attribute\">--bg-color<\/span>: <span class=\"hljs-built_in\">var<\/span>(--light-color);\n}\n\n<span class=\"hljs-keyword\">@media<\/span> (<span class=\"hljs-attribute\">prefers-color-scheme:<\/span> dark) {\n  <span class=\"hljs-selector-pseudo\">:root<\/span> {\n    <span class=\"hljs-attribute\">--text-color<\/span>: <span class=\"hljs-built_in\">var<\/span>(--light-color);\n    <span class=\"hljs-attribute\">--bg-color<\/span>: <span class=\"hljs-built_in\">var<\/span>(--dark-color);\n  }\n}\n\n<span class=\"hljs-selector-tag\">body<\/span> {\n  <span class=\"hljs-attribute\">color<\/span>: <span class=\"hljs-built_in\">var<\/span>(--text-color);\n  <span class=\"hljs-attribute\">background-color<\/span>: <span class=\"hljs-built_in\">var<\/span>(--bg-color);\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-2\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre><\/div>\n\n\n\n<div class=\"wp-block-column is-layout-flow wp-block-column-is-layout-flow\">\n<p>And now you can do this:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-3\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-pseudo\">:root<\/span> {\n  <span class=\"hljs-attribute\">color-scheme<\/span>: light dark;\n\n  <span class=\"hljs-attribute\">--light<\/span>: <span class=\"hljs-number\">#292524<\/span>;\n  <span class=\"hljs-attribute\">--dark<\/span>: <span class=\"hljs-number\">#f5f5f4<\/span>;\n}\n\n<span class=\"hljs-selector-tag\">body<\/span> {\n  <span class=\"hljs-attribute\">color<\/span>: <span class=\"hljs-built_in\">light-dark<\/span>(var(--light), <span class=\"hljs-built_in\">var<\/span>(--dark));\n  <span class=\"hljs-attribute\">background-color<\/span>: <span class=\"hljs-built_in\">light-dark<\/span>(var(--dark), <span class=\"hljs-built_in\">var<\/span>(--light));\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-3\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre><\/div>\n<\/div>\n\n\n\n<p>Essentially, it prevents you from having to use the <code>@media<\/code> query and re-declare variables. I like it. I think it makes code like this more readable and succinct \u2014 pending variable naming and usage of course. <\/p>\n\n\n\n<p>Here&#8217;s what it&#8217;s good for: designing a site that responds to the operating system level setting for light mode \/ dark mode. And you have to be good with the <a href=\"https:\/\/caniuse.com\/mdn-css_types_color_light-dark\">level of browser support<\/a> (no Safari just yet, as I write, but it&#8217;s in preview). If you are wondering what the fallback is, it&#8217;s doing things the old way (see above), and if you&#8217;re going to write that you might as well leave it at that.<\/p>\n\n\n\n<p>If you&#8217;re going to want to offer more themes than just light and dark, well, you&#8217;re out of luck here, you&#8217;ll need to implement something else, likely based on changing classes on the <code>&lt;html&gt;<\/code> element and updating variables when the class matches. <\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-4\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-tag\">html<\/span><span class=\"hljs-selector-class\">.nickleoden-theme<\/span> {\n  <span class=\"hljs-attribute\">--bg-color<\/span>: purple;\n  <span class=\"hljs-attribute\">--text-color<\/span>: green;\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-4\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>I could imagine a more composable toggle method in the future, but this is what we have for now. <\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<p>When I first saw <code>light-dark()<\/code>, I liked the basic idea, but I figured as soon as you offer your own user toggle for mode, you&#8217;d be out of luck. After all, you can&#8217;t change what the <code>prefers-color-scheme<\/code> media query returns, and thus which of the two values <code>light-dark()<\/code> will pick. But it turns out you can! The trick lies in that <code>color-scheme<\/code> property. You&#8217;ll see recommendations that you use <code>color-scheme: light dark;<\/code>. If you only set one or the other, you&#8217;re forcing <code>light-dark()<\/code> to pick the relevant side.<\/p>\n\n\n\n<p>So then your user toggle could force one or the other values on the document if it&#8217;s set. (If it&#8217;s not set, leave it alone!). Here&#8217;s that working:<\/p>\n\n\n\n<div class=\"wp-block-cp-codepen-gutenberg-embed-block cp_embed_wrapper\"><iframe id=\"cp_embed_bGJmgGW\" src=\"\/\/codepen.io\/anon\/embed\/bGJmgGW?height=450&amp;theme-id=47434&amp;slug-hash=bGJmgGW&amp;default-tab=result\" height=\"450\" scrolling=\"no\" frameborder=\"0\" allowfullscreen allowpaymentrequest name=\"CodePen Embed bGJmgGW\" title=\"CodePen Embed bGJmgGW\" class=\"cp_embed_iframe\" style=\"width:100%;overflow:hidden\">CodePen Embed Fallback<\/iframe><\/div>\n\n\n\n<p>I like that this is based on a CSS property, as it means that you can use the cascade if you need to, setting the <code>color-scheme <\/code>on a per-element basis.<\/p>\n\n\n\n<div class=\"wp-block-cp-codepen-gutenberg-embed-block cp_embed_wrapper\"><iframe id=\"cp_embed_YzMJMZx\" src=\"\/\/codepen.io\/anon\/embed\/YzMJMZx?height=450&amp;theme-id=47434&amp;slug-hash=YzMJMZx&amp;default-tab=result\" height=\"450\" scrolling=\"no\" frameborder=\"0\" allowfullscreen allowpaymentrequest name=\"CodePen Embed YzMJMZx\" title=\"CodePen Embed YzMJMZx\" class=\"cp_embed_iframe\" style=\"width:100%;overflow:hidden\">CodePen Embed Fallback<\/iframe><\/div>\n\n\n\n<p>That&#8217;s just cool I think.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<p>Anne Sturdivant <a href=\"https:\/\/weblog.anniegreens.lol\/2024\/04\/what-ive-learned-about-css-color-scheme-and-system-color\">blogged some recent learnings about all this color theme stuff<\/a> and I learned a few things. For one, <code>color-scheme<\/code> also has <code>normal<\/code> which is &#8220;no color schemes defined, default to the browser or OS setting&#8221;, which I would assume means works the same as <code>light dark<\/code>, but in my testing did <em>not<\/em> switch over to dark when I switched my OS \ud83e\udd37. Then there is <code>only<\/code> as a keyword to, uhm,  I guess even more forcibly set the theme? It&#8217;s not clear to me, and also <a href=\"https:\/\/caniuse.com\/mdn-css_properties_color-scheme_only_dark\">not really supported yet<\/a> anyway. <\/p>\n\n\n\n<p>Annie also clearly points out that when you change the <code>color-theme<\/code> away from the default light mode, <em>all sorts<\/em> of stuff changes. It&#8217;s certainly not just the results of <code>light-dark()<\/code>. If you set <code>dark<\/code>, the UI scrollbars go dark, and all the different form controls go dark. In fact, that might be <a href=\"https:\/\/blogs.windows.com\/msedgedev\/2021\/06\/16\/dark-mode-html-form-controls\/\">the number one use-case<\/a> for <code>color-scheme<\/code> really, even if we do have <code>accent-color<\/code> now. <\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-large is-resized\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"921\" src=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/04\/Screenshot-2024-04-18-at-3.59.51%E2%80%AFPM.png?resize=1024%2C921&#038;ssl=1\" alt=\"\" class=\"wp-image-1725\" style=\"width:471px;height:auto\" srcset=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/04\/Screenshot-2024-04-18-at-3.59.51%E2%80%AFPM.png?resize=1024%2C921&amp;ssl=1 1024w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/04\/Screenshot-2024-04-18-at-3.59.51%E2%80%AFPM.png?resize=300%2C270&amp;ssl=1 300w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/04\/Screenshot-2024-04-18-at-3.59.51%E2%80%AFPM.png?resize=768%2C691&amp;ssl=1 768w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/04\/Screenshot-2024-04-18-at-3.59.51%E2%80%AFPM.png?w=1172&amp;ssl=1 1172w\" sizes=\"auto, (max-width: 1000px) 100vw, 1000px\" \/><\/figure>\n<\/div>\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<p>There is also this whole idea of System Colors that tends to come up in writing about this, so I&#8217;m going to do the same. These are &#8220;named&#8221; colors, like <code>rebeccapurple<\/code>, but they have a fancy baked in ability in that they can change when the <code>color-scheme<\/code> changes. So if you want to flip out basically white and black, but not bother with variables and fancy functions and whatnot, you&#8217;ve got:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-5\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-tag\">body<\/span> {\n  <span class=\"hljs-attribute\">background-color<\/span>: Canvas;\n  <span class=\"hljs-attribute\">color<\/span>: CanvasText;\n  <span class=\"hljs-attribute\">color-scheme<\/span>: light dark;\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-5\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Note the <code>Canvas<\/code> and <code>CanvasText<\/code>, those are the System Colors. Allow me to snipe the images of <a href=\"https:\/\/codepen.io\/stoumann\/pen\/GRYNPzy\">the available colors<\/a> and what they look like in the different modes from <a href=\"https:\/\/dev.to\/madsstoumann\/dark-mode-in-3-lines-of-css-and-other-adventures-1ljj\">a great article by Mads Stoumann<\/a>. <\/p>\n\n\n\n<div class=\"wp-block-columns is-layout-flex wp-container-core-columns-is-layout-9d6595d7 wp-block-columns-is-layout-flex\">\n<div class=\"wp-block-column is-layout-flow wp-block-column-is-layout-flow\">\n<figure class=\"wp-block-image size-full\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"800\" height=\"991\" src=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/04\/light-system-colors.png?resize=800%2C991&#038;ssl=1\" alt=\"\" class=\"wp-image-1726\" srcset=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/04\/light-system-colors.png?w=800&amp;ssl=1 800w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/04\/light-system-colors.png?resize=242%2C300&amp;ssl=1 242w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/04\/light-system-colors.png?resize=768%2C951&amp;ssl=1 768w\" sizes=\"auto, (max-width: 800px) 100vw, 800px\" \/><\/figure>\n<\/div>\n\n\n\n<div class=\"wp-block-column is-layout-flow wp-block-column-is-layout-flow\">\n<figure class=\"wp-block-image size-full\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"800\" height=\"991\" src=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/04\/dark-system-colors.png?resize=800%2C991&#038;ssl=1\" alt=\"\" class=\"wp-image-1727\" srcset=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/04\/dark-system-colors.png?w=800&amp;ssl=1 800w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/04\/dark-system-colors.png?resize=242%2C300&amp;ssl=1 242w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/04\/dark-system-colors.png?resize=768%2C951&amp;ssl=1 768w\" sizes=\"auto, (max-width: 800px) 100vw, 800px\" \/><\/figure>\n<\/div>\n<\/div>\n\n\n\n<p>Mads made <a href=\"https:\/\/codepen.io\/stoumann\/pen\/GRYNPzy\">a cool demo<\/a> of a theme toggler that uses newfangled style queries that is worth checking out. Remember, though, that these client-side-only theme switchers are really just demos. It&#8217;s likely on a real-world site, if you&#8217;re offering a switcher, you should try to persist that information. A cookie, sessionStorage, localStorage, to a database&#8230; something. Then when you do that, there is the risk that the page renders before you can access and set that information, <a href=\"https:\/\/css-tricks.com\/flash-of-inaccurate-color-theme-fart\/\">leading to FART<\/a>, my most successful coining of an acronym ever. It&#8217;s a tricky thing, as delaying rendering just for something like this doesn&#8217;t feel right. <\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<p>Another thing percolating in the industry is, as Bramus puts it: <a href=\"https:\/\/www.bram.us\/2024\/04\/13\/what-if-you-had-real-control-over-light-mode-dark-mode-on-a-per-site-basis\/\">What if you had real control over Light Mode \/ Dark Mode on a per-site basis?<\/a> I always say that web standards bodies, and even browsers themselves to some degree, are at their best when they see developers toiling and doing the same sort of things, and then introduce better and standardized methods. Properly implementing color themes and the toggling methods with persistence is real work! It doesn&#8217;t look like color themes are just a fad, so I think this is a clear opportunity for help. <\/p>\n\n\n\n<p>I think a bit of browser UI would be perfectly welcome:<\/p>\n\n\n\n\t\t<figure class=\"wp-block-jetpack-videopress jetpack-videopress-player\" style=\"\" >\n\t\t\t<div class=\"jetpack-videopress-player__wrapper\"> <iframe title=\"VideoPress Video Player\" aria-label='VideoPress Video Player' width='500' height='297' src='https:\/\/videopress.com\/embed\/g9xqgjj6?cover=1&amp;autoPlay=0&amp;controls=1&amp;loop=0&amp;muted=0&amp;persistVolume=1&amp;playsinline=0&amp;preloadContent=metadata&amp;useAverageColor=1&amp;hd=0' frameborder='0' allowfullscreen data-resize-to-parent=\"true\" allow='clipboard-write'><\/iframe><script src='https:\/\/v0.wordpress.com\/js\/next\/videopress-iframe.js?m=1674852142'><\/script><\/div>\n\t\t\t\n\t\t\t\n\t\t<\/figure>\n\t\t\n\n\n<p>A perfect implementation would be that the browser itself remembers what choice you&#8217;ve made (light, dark, or default to system preference), and that is applied during rendering, avoiding FART. Sounds like it is going to require <a href=\"https:\/\/wicg.github.io\/web-preferences-api\/\">a new API<\/a> and then <em>potentially<\/em> browsers actually using it themselves, which is kinda funny to think about. I normally think about web APIs as being for developers, not browsers. <\/p>\n","protected":false},"excerpt":{"rendered":"<p>There has been a fresh round of enthusiasm and writing around light mode \/ dark mode support for the web lately. I think it&#8217;s driven partially by the new light-dark() function in CSS (CSS Color Module Level 5 spec) that makes it easier to declare values that change depending on the mode. Here&#8217;s the basic [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":1732,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"sig_custom_text":"","sig_image_type":"featured-image","sig_custom_image":0,"sig_is_disabled":false,"inline_featured_image":false,"_jetpack_memberships_contains_paid_content":false,"footnotes":""},"categories":[1],"tags":[81,157],"class_list":["post-1716","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-blog-post","tag-color","tag-dark-theme"],"acf":[],"jetpack_featured_media_url":"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/04\/light-dark-thumb.jpg?fit=1000%2C500&ssl=1","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/posts\/1716","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/comments?post=1716"}],"version-history":[{"count":11,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/posts\/1716\/revisions"}],"predecessor-version":[{"id":1736,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/posts\/1716\/revisions\/1736"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/media\/1732"}],"wp:attachment":[{"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/media?parent=1716"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/categories?post=1716"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/tags?post=1716"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}