<!-- mobian-agent-page publisher="dailydev" canonical="https://daily.dev/posts/how-to-build-a-dark-mode-toggle-without-javascript-nbeu3nil3" -->

---
title: How to Build a Dark Mode Toggle Without JavaScript
description: A practical guide to implementing a dark mode toggle using only HTML and CSS, without any JavaScript. The technique leverages the CSS `:has()` pseudo-class...
canonical: https://daily.dev/posts/how-to-build-a-dark-mode-toggle-without-javascript-nbeu3nil3
twitter:card: summary_large_image
twitter:site: @dailydotdev
og:type: website
og:site_name: daily.dev
og:title: How to Build a Dark Mode Toggle Without JavaScript | daily.dev
og:description: A practical guide to implementing a dark mode toggle using only HTML and CSS, without any JavaScript. The technique leverages the CSS `:has()` pseudo-class...
og:url: https://daily.dev/posts/how-to-build-a-dark-mode-toggle-without-javascript-nbeu3nil3
og:image: https://api.daily.dev/og/posts/nbeU3niL3.png
og:image:alt: How to Build a Dark Mode Toggle Without JavaScript
og:image:width: 1200
og:image:height: 630
og:locale: en
---

> ## Documentation Index
> Fetch the complete documentation index at: https://daily.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# How to Build a Dark Mode Toggle Without JavaScript

**[freeCodeCamp](https://daily.dev/sources/freecodecamp)** · 6 min read · 30 upvotes · 6 comments

## Summary

A practical guide to implementing a dark mode toggle using only HTML and CSS, without any JavaScript. The technique leverages the CSS `:has()` pseudo-class combined with hidden radio button inputs to detect user selection and apply theme styles. CSS variables are used to manage color tokens for light and dark themes, and the `prefers-color-scheme` media query ensures the toggle defaults to the user's system preference. The post also explains why building JavaScript-free websites benefits accessibility, SEO, and AI crawler visibility.

## Full article

daily.dev links to this article rather than hosting it. Read it at the original source: <https://www.freecodecamp.org/news/how-to-build-a-dark-mode-toggle-without-javascript>

## Questions this post answers

### how can I build a dark mode toggle with only HTML and CSS, no JavaScript

Use radio button inputs paired with the CSS :has() pseudo-class to detect which option is checked, then apply styles conditionally. For example, :root:has(#mode_dark:checked) can set CSS variables like --bg and --fg, which are then applied via var(--bg) on the body, letting the page switch themes purely through CSS state selectors.

_See the full walkthrough for building JS-free UI toggles curated for developers on daily.dev._

### how does the CSS :has() selector work for detecting a checked radio button

The :has() pseudo-class acts like a parent selector, matching an element if it contains a match for the selector inside the parentheses. Writing html:has(#mode_dark:checked) matches the html element whenever the radio input with id mode_dark is checked anywhere on the page, letting you style unrelated elements based on that checked state.

_Developers exploring modern CSS selectors like this can track such techniques on daily.dev._

### why might a website benefit from working without JavaScript

Sites that work without JavaScript are more accessible to users on old devices or poor connections, and are more reliably indexed by search engines and AI crawlers. Many AI crawlers and answer engines read raw HTML rather than executing JavaScript, so a purely client-side rendered React app may appear as an empty div to such bots unless SSR, SSG, or prerendering is used.

_Anyone weighing SSR versus client-side rendering for SEO can follow these trade-offs on daily.dev._

## Community discussion

Top comments from developers on daily.dev.

**@allinonetools** · 1 upvotes

> **Nice write-up!** It's a great reminder that not every interactive feature needs JavaScript—less code, simpler maintenance, and a faster experience.

**@nubecolectiva** · 0 upvotes

> Thanks for the article, But I heard it's better to do it with pure CSS because of the file size?

**@devmount** · 0 upvotes

> I love the `:has()` pseudo class. It's really useful and was introduced waay to late (baseline end of 2023)!

**@jakubkordovsky** · 0 upvotes

> This is neat, but also kinda useless since you'd want to remember the user's chosen mode, so you still need JS either way to set localStorage or a cookie.

## Similar posts on daily.dev

- [Dark Mode Toggle : Frontend Coding Exercises](https://daily.dev/posts/dark-mode-toggle-frontend-coding-exercises-mapdzpbjg) · Kirupa · 4 upvotes · 0 comments
- [Six levels of dark mode](https://daily.dev/posts/six-levels-of-dark-mode-e6um6rawa) · Hacker News · 1 upvotes · 1 comments
- [Modes and Themes That Stick - Building a Persistent Toggle Solution](https://daily.dev/posts/modes-and-themes-that-stick---building-a-persistent-toggle-solution-70bokb2oo) · Always Twisted · 1 upvotes · 1 comments
- [Build a Persistent Light / Dark Theme in React](https://daily.dev/posts/build-a-persistent-light-dark-theme-in-react-5hbpejx38) · Javarevisited · 4 upvotes · 0 comments

---

Tags: [#css](https://daily.dev/tags/css)

[View this post on daily.dev](https://daily.dev/posts/how-to-build-a-dark-mode-toggle-without-javascript-nbeu3nil3)

```json
{"@context":"https://schema.org","@graph":[{"@type":"Organization","@id":"https://daily.dev/#organization","name":"daily.dev","url":"https://daily.dev","logo":{"@type":"ImageObject","url":"https://daily.dev/apple-touch-icon.png","width":180,"height":180},"sameAs":["https://twitter.com/dailydotdev","https://github.com/dailydotdev","https://www.linkedin.com/company/daily-dev-ltd"]},{"@type":"WebSite","@id":"https://daily.dev/#website","url":"https://daily.dev","name":"daily.dev","publisher":{"@id":"https://daily.dev/#organization"},"potentialAction":{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https://daily.dev/search?q={search_term_string}"},"query-input":"required name=search_term_string"}}]}
{"@context":"https://schema.org","@type":"TechArticle","headline":"How to Build a Dark Mode Toggle Without JavaScript","url":"https://daily.dev/posts/how-to-build-a-dark-mode-toggle-without-javascript-nbeu3nil3","mainEntityOfPage":{"@type":"WebPage","@id":"https://daily.dev/posts/how-to-build-a-dark-mode-toggle-without-javascript-nbeu3nil3"},"datePublished":"2026-07-06T17:05:46.994Z","dateModified":"2026-09-14T09:07:43.038Z","description":"A practical guide to implementing a dark mode toggle using only HTML and CSS, without any JavaScript. The technique leverages the CSS `:has()` pseudo-class...","image":"https://media.daily.dev/image/upload/f_auto,q_auto/v1/posts/ab4ee3deb8f177c3ab58808f5e402155?_a=AQAEuop","thumbnailUrl":"https://media.daily.dev/image/upload/f_auto,q_auto/v1/posts/ab4ee3deb8f177c3ab58808f5e402155?_a=AQAEuop","isAccessibleForFree":true,"articleSection":"freeCodeCamp","inLanguage":"en","publisher":{"@type":"Organization","name":"daily.dev","url":"https://daily.dev","logo":{"@type":"ImageObject","url":"https://daily.dev/apple-touch-icon.png","width":180,"height":180}},"author":{"@type":"Organization","name":"freeCodeCamp","logo":"https://media.daily.dev/image/upload/t_logo,f_auto/v1628412854/logos/freecodecamp","url":"https://daily.dev/sources/freecodecamp"},"commentCount":6,"discussionUrl":"https://daily.dev/posts/how-to-build-a-dark-mode-toggle-without-javascript-nbeu3nil3","interactionStatistic":[{"@type":"InteractionCounter","interactionType":{"@type":"LikeAction"},"userInteractionCount":30},{"@type":"InteractionCounter","interactionType":{"@type":"CommentAction"},"userInteractionCount":6}],"keywords":"css","timeRequired":"PT6M"}
{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https://daily.dev"},{"@type":"ListItem","position":2,"name":"freeCodeCamp","item":"https://daily.dev/sources/freecodecamp"},{"@type":"ListItem","position":3,"name":"How to Build a Dark Mode Toggle Without JavaScript"}]}
{"@context":"https://schema.org","@type":"WebPage","@id":"https://daily.dev/posts/how-to-build-a-dark-mode-toggle-without-javascript-nbeu3nil3","comment":[{"@type":"Comment","text":"Nice write-up! It’s a great reminder that not every interactive feature needs JavaScript—less code, simpler maintenance, and a faster experience.","datePublished":"2026-07-08T03:15:10.340Z","url":"https://daily.dev/posts/nbeU3niL3#c-wXPJWwXek","author":{"@type":"Person","name":"AllInOneTools","url":"https://daily.dev/allinonetools","image":"https://media.daily.dev/image/upload/s--4ZT8QlF0--/f_auto/v1769790028/avatars/avatar_igzBsWloJeaegzm9bQltZ?_a=BAMAMiiu0"},"interactionStatistic":{"@type":"InteractionCounter","interactionType":{"@type":"LikeAction"},"userInteractionCount":1}},{"@type":"Comment","text":"Thanks for the article, But I heard it’s better to do it with pure CSS because of the file size?","datePublished":"2026-07-10T13:38:55.145Z","url":"https://daily.dev/posts/nbeU3niL3#c-zerfyVsZM","author":{"@type":"Person","name":"Nube Colectiva","url":"https://daily.dev/nubecolectiva","image":"https://media.daily.dev/image/upload/s--sKadI8aB--/f_auto/v1787001795/avatars/avatar_d2lQlbHPWLBIYkmIhoKKS?_a=BAMAMicg0"}},{"@type":"Comment","text":"I love the :has() pseudo class. It’s really useful and was introduced waay to late (baseline end of 2023)!","datePublished":"2026-07-09T05:19:36.291Z","url":"https://daily.dev/posts/nbeU3niL3#c-mSOHN1JBK","author":{"@type":"Person","name":"Andreas","url":"https://daily.dev/devmount","image":"https://media.daily.dev/image/upload/s--0yHQgkD3--/f_auto/v1730121566/avatars/avatar_wkPuMtV83z5WseTrVc5QU"}},{"@type":"Comment","text":"This is neat, but also kinda useless since you’d want to remember the user’s chosen mode, so you still need JS either way to set localStorage or a cookie.","datePublished":"2026-07-07T04:54:01.419Z","url":"https://daily.dev/posts/nbeU3niL3#c-ERm84VMJe","author":{"@type":"Person","name":"Jakub Kordovský","url":"https://daily.dev/jakubkordovsky","image":"https://avatars.githubusercontent.com/u/11707547?v=4"}}]}
{"@context":"https://schema.org","@type":"FAQPage","@id":"https://daily.dev/posts/how-to-build-a-dark-mode-toggle-without-javascript-nbeu3nil3#faq","mainEntity":[{"@type":"Question","name":"how can I build a dark mode toggle with only HTML and CSS, no JavaScript","acceptedAnswer":{"@type":"Answer","text":"Use radio button inputs paired with the CSS :has() pseudo-class to detect which option is checked, then apply styles conditionally. For example, :root:has(#mode_dark:checked) can set CSS variables like --bg and --fg, which are then applied via var(--bg) on the body, letting the page switch themes purely through CSS state selectors. See the full walkthrough for building JS-free UI toggles curated for developers on daily.dev."}},{"@type":"Question","name":"how does the CSS :has() selector work for detecting a checked radio button","acceptedAnswer":{"@type":"Answer","text":"The :has() pseudo-class acts like a parent selector, matching an element if it contains a match for the selector inside the parentheses. Writing html:has(#mode_dark:checked) matches the html element whenever the radio input with id mode_dark is checked anywhere on the page, letting you style unrelated elements based on that checked state. Developers exploring modern CSS selectors like this can track such techniques on daily.dev."}},{"@type":"Question","name":"why might a website benefit from working without JavaScript","acceptedAnswer":{"@type":"Answer","text":"Sites that work without JavaScript are more accessible to users on old devices or poor connections, and are more reliably indexed by search engines and AI crawlers. Many AI crawlers and answer engines read raw HTML rather than executing JavaScript, so a purely client-side rendered React app may appear as an empty div to such bots unless SSR, SSG, or prerendering is used. Anyone weighing SSR versus client-side rendering for SEO can follow these trade-offs on daily.dev."}}]}
```

