<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Kirill Ivanov</title>
    <description>The latest articles on DEV Community by Kirill Ivanov (@kirillunlimited).</description>
    <link>https://dev.to/kirillunlimited</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3193132%2F73c0be49-e628-463f-b765-c8a9fee32db8.jpg</url>
      <title>DEV Community: Kirill Ivanov</title>
      <link>https://dev.to/kirillunlimited</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/kirillunlimited"/>
    <language>en</language>
    <item>
      <title>The Rise of Hybrid Frameworks</title>
      <dc:creator>Kirill Ivanov</dc:creator>
      <pubDate>Thu, 22 May 2025 19:27:39 +0000</pubDate>
      <link>https://dev.to/kirillunlimited/the-rise-of-hybrid-frameworks-5dkb</link>
      <guid>https://dev.to/kirillunlimited/the-rise-of-hybrid-frameworks-5dkb</guid>
      <description>&lt;p&gt;Just a decade ago, web development was straightforward: all rendering was done server-side, meaning each click on a hyperlink loaded a new page with fully-rendered HTML. This thin client setup allowed browsers to seamlessly handle navigation and made the system ideally suited for search engine indexing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Era of SPA
&lt;/h2&gt;

&lt;p&gt;As the internet evolved, so did user expectations. Websites were no longer just static pages of information, but needed to be interactive with features such as real-time updates and dynamic content. This demand led to the creation of Single Page Applications (SPA), where the backend transformed into an API serving data, and the frontend handled rendering and interactions.&lt;/p&gt;

&lt;p&gt;In mid-2000s Gmail revolutionized web development by becoming the first true SPA project. Its seamless user experience inspired developers worldwide to adopt this new model, leading to the creation of frameworks like &lt;a href="https://react.dev/" rel="noopener noreferrer"&gt;React&lt;/a&gt;, &lt;a href="https://angular.dev/" rel="noopener noreferrer"&gt;Angular&lt;/a&gt; and &lt;a href="https://vuejs.org/" rel="noopener noreferrer"&gt;Vue.js&lt;/a&gt;. SPAs became the go-to solution for many applications, which required real-time interactions and a fluid user experience.&lt;/p&gt;

&lt;h2&gt;
  
  
  SPA Challenges
&lt;/h2&gt;

&lt;p&gt;Despite their advantages, SPAs presented several challenges. The client-side rendering model conflicted with traditional web conventions, leading to complications with browser navigation and SEO indexing. Developers had to manage navigation history manually, and static URLs became a problem for link sharing. Additionally, the increase in network interactions led to more errors, requiring robust error-handling mechanisms.&lt;/p&gt;

&lt;p&gt;One of the promises of SPAs was faster performance, as only raw data needed to be loaded, with the client handling the rest. However, advancements in HTTP protocols, browser capabilities, and caching soon diminished this advantage. Issues with waiting times and the overuse of loading spinners created a perception of constant loading, affecting the user experience.&lt;/p&gt;

&lt;p&gt;SPAs also necessitated the duplication of backend logic on the frontend. Validation, business rules, and functionalities had to be replicated, effectively doubling the developers’ workload. This duplication led to increased complexity and slower development speeds, highlighting the need for a more efficient approach.&lt;/p&gt;

&lt;h2&gt;
  
  
  Returning to Server-Side Rendering
&lt;/h2&gt;

&lt;p&gt;As the limitations of SPAs became more apparent, the frontend community began to revisit SSR as a viable and often superior alternative.&lt;/p&gt;

&lt;p&gt;The popularisation of SSR among frontend developers can be largely attributed to the widespread adoption of frameworks with server-side rendering. These frameworks provide an elegant integration of SSR with modern JavaScript libraries and frameworks like React and Vue.js. &lt;a href="https://nextjs.org/" rel="noopener noreferrer"&gt;Next.js&lt;/a&gt;, for instance, has become a de facto choice for many React developers seeking to leverage SSR's benefits without sacrificing the advantages of a component-based architecture. By abstracting much of the complexity involved in SSR, these frameworks have made it accessible and practical for everyday use in web development.&lt;/p&gt;

&lt;p&gt;One of the key factors driving the shift towards SSR is the improvement in initial load times and the overall user experience. With SPAs, the client must download and execute a large JavaScript bundle before any meaningful content can be displayed, leading to longer wait times. In contrast, SSR provides fully rendered HTML from the server, allowing users to see and interact with content almost immediately. This not only enhances the user experience but also plays a crucial role in performance metrics that impact search engine rankings and user retention.&lt;/p&gt;

&lt;p&gt;The SEO benefits of SSR have also played a significant role in its resurgence. Search engine optimization is a critical aspect of web development, as higher visibility in search engine results can drive more traffic to a website. SPAs often struggle with SEO because search engine bots can have difficulty indexing dynamically generated content. SSR addresses this issue by serving pre-rendered HTML that search engines can easily crawl and index, leading to better search performance and higher organic traffic.&lt;/p&gt;

&lt;h2&gt;
  
  
  SSR Pitfalls
&lt;/h2&gt;

&lt;p&gt;While SSR addresses many of the shortcomings of SPAs, it also introduces its own set of challenges. One of the primary issues is the increased server load. Because each user request requires the server to render the HTML before sending it to the client, SSR can be more resource-intensive, especially for high-traffic applications. This necessitates careful consideration of server scalability and performance optimization to handle increased demands effectively.&lt;/p&gt;

&lt;p&gt;Unlike SPAs, which can provide a highly interactive experience from the start, SSR pages may be less interactive until the client-side JavaScript takes over. This can lead to a temporary lag in interactivity, known as the "re-hydration" phase, where the client-side JavaScript enhances the static HTML with interactive features.&lt;/p&gt;

&lt;p&gt;Also, managing application state can be more complex in SSR, as developers need to ensure that state is properly synchronized between the server and client. This can involve additional code to hydrate the client-side application with the server-rendered state, adding complexity to the development process.&lt;/p&gt;

&lt;p&gt;Finally, SSR development can be more challenging to debug and develop compared to SPAs. The need to manage code that runs on both the server and the client can introduce additional complexity, requiring a deeper understanding of both environments.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Rise of Hybrid Frameworks
&lt;/h2&gt;

&lt;p&gt;Developers began to explore a hybrid approach that combined the best of both worlds. This approach involved returning to classic server-side HTML generation using backend frameworks, supplemented by data attributes and minimal JavaScript.&lt;/p&gt;

&lt;p&gt;At the forefront of this hybrid model stands &lt;a href="https://htmx.org/" rel="noopener noreferrer"&gt;htmx&lt;/a&gt;, a pioneering framework that champions a simplified approach to dynamic web development. By leveraging data attributes, htmx grants developers the ability to control client-side interactions directly from the HTML markup. This means that the primary focus shifts back to generating valid HTML on the server, while htmx seamlessly inserts and updates this content within the DOM as needed. Consequently, developers can maintain a clean and straightforward server-side codebase while still delivering interactive and responsive user experiences on the client side.&lt;/p&gt;

&lt;p&gt;Taking the hybrid approach a step further, &lt;a href="https://stimulus.hotwired.dev/" rel="noopener noreferrer"&gt;Stimulus&lt;/a&gt; enriches the server-rendered content with dynamic interactions that transcend the capabilities of traditional SSR alone. By introducing behavior-driven development, Stimulus empowers developers to augment existing HTML components with minimal JavaScript code. This enables the creation of rich, interactive interfaces without sacrificing the benefits of server-side rendering.&lt;/p&gt;

&lt;p&gt;Additionally, the &lt;a href="https://qwik.dev/" rel="noopener noreferrer"&gt;Qwik&lt;/a&gt; framework has entered the scene, offering an innovative solution to the hybrid model. Qwik is designed to deliver instant loading web applications by using a resumable architecture, which allows web applications to start up with minimal JavaScript. It achieves this by splitting the application code into small, lazy-loaded chunks that only activate when needed, thus optimizing both the performance and user experience.&lt;/p&gt;

&lt;p&gt;Essentially, these frameworks signal a beginning of a new era in web development. They leverage the advancements in web technologies to offer a more balanced approach, integrating both client and server-side rendering. This hybrid model promises to address the challenges posed by SPAs while retaining the benefits of interactive and dynamic web applications.&lt;/p&gt;

&lt;h2&gt;
  
  
  Future of Web Development
&lt;/h2&gt;

&lt;p&gt;The evolution of web development reflects the ongoing quest for balance and efficiency. From the simplicity of server-side rendering to the interactivity of SPAs, and now to the promising hybrid models, each phase has brought valuable lessons. As the industry continues to evolve, hybrid approach is likely to become the standard, providing the best of both server-side and client-side rendering.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;This article was moved here from &lt;a href="https://kirillunlimited.com" rel="noopener noreferrer"&gt;my personal website&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>htmx</category>
      <category>stimulus</category>
      <category>qwik</category>
    </item>
    <item>
      <title>Stumbling Blocks of Backend-Driven UI on Web</title>
      <dc:creator>Kirill Ivanov</dc:creator>
      <pubDate>Thu, 22 May 2025 19:23:20 +0000</pubDate>
      <link>https://dev.to/kirillunlimited/stumbling-blocks-of-backend-driven-ui-on-the-web-12bo</link>
      <guid>https://dev.to/kirillunlimited/stumbling-blocks-of-backend-driven-ui-on-the-web-12bo</guid>
      <description>&lt;p&gt;Backend-driven UI (BDUI, also known as server-driven UI) is a powerful concept for building modern applications instead of the classical client-server architecture. It implies that the backend dictates not only &lt;strong&gt;what&lt;/strong&gt; to render, but also &lt;strong&gt;how&lt;/strong&gt; it should look like. In other words, BDUI is a quintessence of a “thin client” architecture. Technically, the server response contains not just raw data, but also detailed information about each UI element on the page, its position in the layout, description of its behaviour and reactions to user actions.&lt;/p&gt;

&lt;p&gt;This approach has a lot of advantages such as less code on the client, a single source of truth for different platforms and simplified AB-testing. But I believe that the main feature of BDUI is its blazingly fast time to market. This advantage is due to the fact that ideally all new functionality should be delivered as a new response from the backend. Thus, client applications don’t need to make any updates if they already know how to process response with such structure. This solution is very suitable for mobile platforms: it allows to drastically improve feature delivery speed, because mobile developers avoid the need to publish their application in the marketplace and wait for users to install the update.&lt;/p&gt;

&lt;p&gt;It may seem like a good idea to develop a single BDUI platform for both mobile and web applications. Obviously, business customers would be happy to be able to manage the view of each client from the single CMS provided by the backend. Of course, this whole concept has some common disadvantages, such as strict design limitations, worse responsiveness and increased complexity of testing and versioning. But there are some important stumbling blocks that are exclusive to the web platform. They can negate the benefits of cross-platform architecture in the long run. I have a small experience of implementing BDUI methodology in a large e-commerce project. In this article I would like to share the main drawbacks that I wish I had known about before starting building a BDUI web application.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cross-Platform Layout
&lt;/h2&gt;

&lt;p&gt;Basically, mobile layout implies the arrangement of widgets along one axis – the vertical axis. If product managers and designers don’t have special wishes for unique arrangement of elements for different mobile operating systems, this one-dimensional layout can be common to all mobile platforms. It is really convenient, as you only need to care about this source of truth for the interface, which greatly simplifies the work of both designers and developers. But if the desktop web appears among the platforms, its two-dimensional layout must now be taken into account. API can no longer be generic, which leads to certain complex and unobvious decisions that complicate back-end support. As a result, one of the key advantages of the backend-centric approach – its platform agnosticism – is lost.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tree-Shaking
&lt;/h2&gt;

&lt;p&gt;If you really care about the performance of your web application, you may encounter some optimization challenges related to proper code splitting. There is no such problem in mobile apps, as user needs to download the whole application before using it. In the case of the web, things work differently: ideally, front-end application should be splitted into a small chunks that would be downloaded by the client on demand. It leads to a certain constraints that, again, force the backend to care about the content for each particular platform.&lt;/p&gt;

&lt;p&gt;If you work with BDUI, sooner or later you will face the necessity to render widgets of the same type, but differing in some features. It can be literally any types of elements: from simple static blocks to complex fields and forms. The easiest way to solve this issue is to implement some kind of widget factory. The basic idea is to have a method that renders some similar widgets according to a set of different configuration values that are dictated by the backend. This technique can be easily implemented within the modern component-based approach using high-order components (HOCs). As a result, you don’t need to create a bunch of identical components. Instead, you have one big component, that generates more specific and customisable components.&lt;/p&gt;

&lt;p&gt;For simplicity, let's consider an example of icons. The backend sends a certain icon alias to the client. The client probably should have some simple key-value data structure that allows the icon component to be identified by the received alias:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;SuccessIcon&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./success&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;WarningIcon&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./warning&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;FailIcon&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./fail&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;IconComponents&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;success&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;SuccessIcon&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;warning&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;WarningIcon&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;fail&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;FailIcon&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Maintaining this list is not a big problem, since it can be generated automatically by the design library tools. But the real problem is that generic icon component cannot predict which exact icon component will be needed at actual runtime, so every single icon will be included into a bundle. In React we will have a high-order component that will render an icon based on the alias value passed in. It will look something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;IconComponents&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./icons&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Icon&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;iconName&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;IconComponents&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;iconName&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The tree-shaking won’t work: the user will download every icon component even though only one of them will be rendered. Imagine if your design kit contains more than a hundred icons. It would certainly affect the overall performance.&lt;/p&gt;

&lt;p&gt;In this particular case, there is a simple solution – do not use components for icons, and just use urls to these images, which will be stored somewhere on CDN. Admittedly, this comes with some other limitations, such as the difficulty of changing the color of the icon, although there are more reasonable solutions to this problem as well. Either way, it puts a spoke in the wheel of developer experience as there are a plenty of other elements, not only icons.&lt;/p&gt;

&lt;h2&gt;
  
  
  Page Load Time
&lt;/h2&gt;

&lt;p&gt;Another big issue is the optimization of initial page load time. The initial page load for a BDUI application can be much slower, because the backend should generate the entire layout dynamically in a single response. This can negatively impact user engagement and SEO rankings which is crucial for any web application. Solving this problem can be a real headache. You may think of a tricky data subloading in separate requests as a simple solution, but then there may be a problem of inconsistency of widgets on the page. And now imagine you have to explain this issue to your back-end developers who have to deal with it: they won't like it at all.&lt;/p&gt;

&lt;h2&gt;
  
  
  Scalability and Maintainability
&lt;/h2&gt;

&lt;p&gt;It is likely that implementing this concept on the web could lead to over-engineering. Instead of creating new awesome front-end features, developers will have to maintain complicated data structures from the server. Obviously, this will become a routine for any front-end developer, as supporting a “dumb” ui-system is not fun at all. It might seem exciting at first, but you are bound to realize soon enough that the development process is going to be a torture. Even though business customers gain profit, developers will either endure and suffer or simply quit the project in search of better opportunities. Eventually, it will be hard to find an experienced and ambitions developer who would agree to maintain and support this kind of project.&lt;/p&gt;

&lt;p&gt;Unfortunately, I can't give any advice to avoid such issues. Exciting challenges will be encountered only at the very beginning, mostly related to the design and building this system while solving a lot of low-level engineering problems. But further maintenance and support will be demotivating with its monotony, offering no benefits for the developer's experience and leading to work fatigue and burnout.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;In this article I highlighted the biggest problems of BDUI from the web perspective. The issue of the long release cycle of mobile apps can certainly be solved with this concept, but this is not the case for the web – we just don't have that kind of problem. As a result, the BDUI concept along with the cross-platform approach introduces artificial obstacles that front-end developers have to overcome without gaining any valuable advantages. It means one step forward for mobile and one step back for web. You should think carefully before agreeing to implement BDUI in your web project and see if the benefits really outweigh all of the difficulties. Probably, it is a good idea to come up with a hybrid back-end service capable of producing different data sets: one for BDUI-based mobile platforms, and the other for the web, adhering to the classical "client-server" approach.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;This article was moved here from &lt;a href="https://kirillunlimited.com" rel="noopener noreferrer"&gt;my personal website&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Native Dark Mode on Web</title>
      <dc:creator>Kirill Ivanov</dc:creator>
      <pubDate>Thu, 22 May 2025 15:15:43 +0000</pubDate>
      <link>https://dev.to/kirillunlimited/native-dark-mode-implementation-4ji0</link>
      <guid>https://dev.to/kirillunlimited/native-dark-mode-implementation-4ji0</guid>
      <description>&lt;p&gt;It is hard to deny that dark mode has been on a hype train for the last few years. Not only websites and apps started to allow users to choose desired color scheme, but also devices themselves cannot do without this feature. Some might assume, that it is only about aesthetics, but you should not forget about readability reasons: vast majority of users prefer to use the dark mode at dark time as it feels more comfortable for eyes.&lt;/p&gt;

&lt;p&gt;So, if you want to embed a color scheme switching control in your web application, check out this step-by-step guide describing a clear and easy to implement technique.&lt;/p&gt;

&lt;h2&gt;
  
  
  Scheme variables
&lt;/h2&gt;

&lt;p&gt;First of all, let’s create separate &lt;code&gt;.css&lt;/code&gt; files with the list of css-variables for each color scheme:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nd"&gt;:root&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;--background-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;white&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;--text-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;black&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nd"&gt;:root&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;--background-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;black&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;--text-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;white&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And use these variables wherever necessary:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nt"&gt;body&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;background-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--background-color&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--text-color&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note, that you shouldn’t place these variables into common styles bundle. Keep them in separate files and tell browser to choose the proper set of variables automatically according to the device’s color scheme. This behaviour can be achieved with &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-color-scheme" rel="noopener noreferrer"&gt;prefers-color-scheme&lt;/a&gt; media feature:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
  ...
  &lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"stylesheet"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"css/light.css"&lt;/span&gt; &lt;span class="na"&gt;media=&lt;/span&gt;&lt;span class="s"&gt;"(prefers-color-scheme: light)"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"stylesheet"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"css/dark.css"&lt;/span&gt; &lt;span class="na"&gt;media=&lt;/span&gt;&lt;span class="s"&gt;"(prefers-color-scheme: dark)"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"stylesheet"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"css/styles.css"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  ...
&lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now you can switch color mode in your device’s settings and see how page switch colors. Don’t forget to add &lt;code&gt;transition&lt;/code&gt; property for every element on the page that depends on color scheme for smoother user experience.&lt;/p&gt;

&lt;h2&gt;
  
  
  Fallback
&lt;/h2&gt;

&lt;p&gt;According to &lt;a href="https://caniuse.com/?search=prefers-color-scheme" rel="noopener noreferrer"&gt;Can I use&lt;/a&gt;, more than 95% of browsers already support &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-color-scheme" rel="noopener noreferrer"&gt;prefers-color-scheme&lt;/a&gt;. If you care about the rest of the browsers, you should definitely come up with some fallback.&lt;/p&gt;

&lt;p&gt;The simplest approach is just to inject light or dark scheme variables to your main css bundle so that your app could use them by default. The obvious drawback is that the same variables will be delivered twice for modern browsers: via your bundled css file and also via separate scheme-specific files. If you have a really huge amount of variables it might hurt performance a little bit.&lt;/p&gt;

&lt;p&gt;Better solution is to check, if browser supports this feature with JavasScript. Otherwise, we will link CSS file with desired color scheme variables manually:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;matchMedia&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;(prefers-color-scheme: dark)&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;matches&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;head&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;insertAdjacentHTML&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;beforeend&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;&amp;lt;link rel="stylesheet" href="css/light.css"&amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Manual switching
&lt;/h2&gt;

&lt;p&gt;But there is a plenty of users who prefer to choose certain color scheme instead of system’s default setup. We need to add a control for color scheme switching. It should be something like a group of radio buttons. Choose whatever you want and whatever suits your design guidelines. For simplicity, let’s use browser’s native radio buttons.&lt;/p&gt;

&lt;p&gt;Most of the sites have only 2 modes: &lt;strong&gt;light&lt;/strong&gt; and &lt;strong&gt;dark&lt;/strong&gt;. But we want to not only allow to choose certain scheme, but also provide option to reset it to system’s default. So there should be 3 options: &lt;strong&gt;light&lt;/strong&gt;, &lt;strong&gt;dark&lt;/strong&gt; and &lt;strong&gt;auto&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
  ...
  &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"radio"&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"color-scheme"&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"light"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"radio"&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"color-scheme"&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"dark"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"radio"&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"color-scheme"&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"auto"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  ...
&lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Let’s dive into coding
&lt;/h2&gt;

&lt;p&gt;We need to implement following steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Init page with proper color scheme

&lt;ol&gt;
&lt;li&gt;If user has already toggled it manually, then apply it&lt;/li&gt;
&lt;li&gt;Otherwise, keep system’s default scheme&lt;/li&gt;
&lt;/ol&gt;


&lt;/li&gt;

&lt;li&gt;Init color scheme switch controls that will save user’s choice in future&lt;/li&gt;

&lt;/ol&gt;

&lt;p&gt;Let’s add some extra data-attributes to CSS link tags to make it easier to find them with JavaScript later:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
  ...
  &lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"stylesheet"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"css/light.css"&lt;/span&gt; &lt;span class="na"&gt;media=&lt;/span&gt;&lt;span class="s"&gt;"(prefers-color-scheme: light)"&lt;/span&gt; &lt;span class="na"&gt;data-color-scheme=&lt;/span&gt;&lt;span class="s"&gt;"light"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"stylesheet"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"css/dark.css"&lt;/span&gt; &lt;span class="na"&gt;media=&lt;/span&gt;&lt;span class="s"&gt;"(prefers-color-scheme: dark)"&lt;/span&gt; &lt;span class="na"&gt;data-color-scheme=&lt;/span&gt;&lt;span class="s"&gt;"dark"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  ...
&lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let’s declare some constants with common data and references to CSS link tags:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;SCHEMES&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;LIGHT&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;light&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;DARK&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dark&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;AUTO&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;auto&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;LOCAL_STORAGE_KEY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;scheme&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;lightStyles&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;link[rel=stylesheet][data-color-scheme="light"]&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;darkStyles&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;link[rel=stylesheet][data-color-scheme="dark"]&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We need to remember user’s color scheme choice to be able to get this value on every page load. For this purpose we will use local storage. Let’s add some helper methods:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;getSavedColorScheme&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;localStorage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;LOCAL_STORAGE_KEY&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;SCHEMES&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;AUTO&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;saveColorScheme&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;scheme&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;localStorage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;LOCAL_STORAGE_KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;scheme&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let’s add a new method &lt;code&gt;switchMedia&lt;/code&gt; that changes &lt;code&gt;media&lt;/code&gt; content of each CSS link tag to tell the browser which color scheme to choose:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;switchMedia&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;scheme&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;switch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;scheme&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="nx"&gt;SCHEMES&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;LIGHT&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="nx"&gt;lightStyles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;media&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;all&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="nx"&gt;darkStyles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;media&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;not all&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="nx"&gt;SCHEMES&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;DARK&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="nx"&gt;lightStyles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;media&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;not all&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="nx"&gt;darkStyles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;media&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;all&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="nx"&gt;SCHEMES&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;AUTO&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="nx"&gt;lightStyles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;media&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;(prefers-color-scheme: light)&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="nx"&gt;darkStyles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;media&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;(prefers-color-scheme: dark)&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;default&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Don’t forget to add an event listener to your color scheme switch control and set initial value:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;initColorSchemeControls&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;scheme&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getSavedColorScheme&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;colorSchemeControls&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelectorAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;input[name="color-scheme"]&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nx"&gt;colorSchemeControls&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;control&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;control&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;scheme&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;control&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;checked&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nx"&gt;control&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;change&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

      &lt;span class="nf"&gt;switchMedia&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nf"&gt;saveColorScheme&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We are almost there. Now we just need to set the proper color scheme on page load with &lt;code&gt;initColorScheme&lt;/code&gt; method:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;initColorScheme&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;scheme&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getSavedColorScheme&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="nf"&gt;switchMedia&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;scheme&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We are done! Now we should call both &lt;code&gt;initColorScheme&lt;/code&gt; and &lt;code&gt;initColorSchemeControls&lt;/code&gt;. It should work like a charm.&lt;/p&gt;

&lt;h2&gt;
  
  
  Flash of Inaccurate Color Theme
&lt;/h2&gt;

&lt;p&gt;If you added all of the JavaScript code into the end of your HTML or added &lt;code&gt;async&lt;/code&gt; attribute to your script tag, you would probably notice a little problem. If you select color scheme other than system’s default scheme, then on each page load there will be a &lt;a href="https://css-tricks.com/flash-of-inaccurate-color-theme-fart/" rel="noopener noreferrer"&gt;Flash of Inaccurate Color Theme&lt;/a&gt; which may worsen UX. It happens because browser fails to execute JavaScript code before the initial page load. You can solve this problem by simply adding your code at the top of your HTML document. It will block render until your JS code is completely executed. But don’t forget, that &lt;code&gt;initColorSchemeControls&lt;/code&gt; should be called only after DOM is completely loaded:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nf"&gt;initColorScheme&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;DOMContentLoaded&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;initColorSchemeControls&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Be careful: this approach may affect First Contentful Paint, so you should keep this scheme setup script really tiny. Don’t load all of your JS bundle before page render. Or if you really don’t want to delay page render, you should probably use some server-side solution.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;That's it! Now we have some nasty and powerful color mode toggle mechanism. By default it delivers system’s preferred color scheme, but it also considers user’s choice. You can check the real-world example of this functionality in the &lt;a href="https://github.com/kirillunlimited/kirillunlimited.com" rel="noopener noreferrer"&gt;source code&lt;/a&gt; of my personal website.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;This article was moved here from &lt;a href="https://kirillunlimited.com" rel="noopener noreferrer"&gt;my personal website&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>html</category>
      <category>css</category>
      <category>darkmode</category>
      <category>colorscheme</category>
    </item>
  </channel>
</rss>
