
The Quest for the Perfect Dark Mode (Gatsby/React) - joshwcomeau
https://joshwcomeau.com/gatsby/dark-mode/
======
joshwcomeau
Hey thread, author here.

I don't have the energy to debunk some of these replies, but let me quickly
say:

1\. The reason that this is a hard problem has nothing to do with React, or
Gatsby. It has to do with the strategy of precompiling HTML and not having a
runtime server, an approach with many many benefits (described here:
[https://joshwcomeau.com/gatsby/a-static-
future/](https://joshwcomeau.com/gatsby/a-static-future/)). If you're
wondering why anyone would put up with all this tomfoolery for a dark mode,
please read that so you can understand the benefits to this approach.

2\. "React is overkill for static sites" is a debate I am not trying to have,
but I can say that for my blog (the site that this post is about), React has
allowed me to do lots of cool things that would have been much harder
otherwise. And using Gatsby means that I have first-meaningful-paint
performance that beats a PHP template with no JS, since my site doesn't have
to do any database lookups or real-time HTML generation. I am not saying that
this is the right approach for every website, but it was 100% the right choice
for this one.

4\. Many of these replies are making suggestions that are discussed in the
article itself. CSS variables are not an alternative to this solution, they
_are_ the solution (my solution wouldn't work without them!). Please read the
post before forming an opinion about it.

I hope that helps clarify some things! Hope you're all doing alright and
staying home.

~~~
edhelas
> When you run gatsby build (or whatever your command is to build your site
> for production), the build system will make all the API calls needed to
> fetch the data necessary to generate every possible HTML page. It'll use
> React server-rendering APIs to turn a tree of React components into a big
> HTML document.

That's what we're calling "cache" in good ol' backend-code applications. It's
used for at least 15 years in e-commerce, blogs and many other "outdated
because now we have react-angular-vue.npm.io" CMS.

With "cache" you can store precompiled HTML pages server side, even only parts
of it (if you have 50.000 articles like you're explaining in your article).

You can even do that in front of your PHP/Ruby/Python app using HTTP caching
proxies such as Squid. Here it's even beyond "blistering fast", it's "ultra
blistering fast".

Do a static website, add a little bit of vanilla JS, want to refresh parts of
the pages ? It's called Ajax.

The moment I saw ReactJS redering moving server side to "pre-render" stuff I
saw that we we just looping again and again.

And again once your HTML is delivered, no one is stopping you to do a few line
of JS to add a simple class on top of your page to toggle the Night Mode. Want
to do it before the JS is executed (and don't have this ugly delay) ? Use a
cookie and set it on render time. If the user is forbidding cookies, well then
it's because he don't want to be tracked and don't want all those fancy
features. You can fallback to prefers-color-scheme.

Things were done simple, no need to reinvent everything each time :)

~~~
pcr910303
> Do a static website, add a little bit of vanilla JS, want to refresh parts
> of the pages ? It's called Ajax.

Before you become sarcastic, try to check out what Gatsby is. Gatsby _is_ a
static website generator that makes bunch of static HTML leaves a bit of
vanilla JS, and progressively enhances the site.

> Things were done simple, no need to reinvent everything each time :)

Things are there for a reason, why not just go to the 70s/80s where there was
no networking, servers, or bloated JS?

~~~
edhelas
> Gatsby is a static website generator that makes bunch of static HTML leaves
> a bit of vanilla JS, and progressively enhances the site.

That was exactly my point. We are currently seeing the whole thing looping :)

> Things are there for a reason, why not just go to the 70s/80s where there
> was no networking, servers, or bloated JS?

React/Angular/Vue can be used for very specific use cases indeed and can be
really powerful tools. But lets face it, for most of the websites, you don't
need those tools. It's even worst most of the time regarding browser
performances, accessibility , SEO, navigation.

Developers are always looking for shinning things. And in the end you end up
with really complex architecture.

I'm just saying that in 95% of the cases. You don't need those tools. But only
simple "old techs" that are battle tested and works flawlessly.

~~~
pcr910303
> That was exactly my point. We are currently seeing the whole thing looping.

If Gatsby is exactly the format you like, what's the problem? The fact that it
uses React as a dependency? Because... it uses 'npm', the worst package
manager of all history?

> But let's face it, for most of the websites, you don't need those tools.

I'm pretty sure asking this is against the guidelines, but I just can't resist
- have you ever worked with jQuery/vanilla JS and React?

If you ever, ever worked with them, one can easily see that the component
model React provides gives a great productivity boost.

For example, consider this[0] code from the old version of this blog: it
defines a 'Code' component that allows live previews of JS code. It would be
super tedious to do that every time you embed a JS example in your post.

> It's even worst most of the time regarding browser performances,
> accessibility , SEO, navigation.

No, it isn't. Gatsby is a 'static site generator', with most of the advantages
and disadvantages that they provide. Navigation is faster, not slower if
you've turned on JS (due to progressive enhancement) and accessibility has
nothing to do with React - it's the matter of proper markup (which React can
do pretty well).

[0]
[https://github.com/joshwcomeau/blog/blob/master/src/componen...](https://github.com/joshwcomeau/blog/blob/master/src/components/Code/Code.js#L29)

------
sneak
Reading this site reminded me of this:

[https://motherfuckingwebsite.com/](https://motherfuckingwebsite.com/)

It's also worth mentioning that TFA's webpage _is broken_.

Despite all his dozens of lines of javascript for his "dark background"
feature, it _totally fails to render_ (resulting in a blindingly white blank
page, ha!) if you have cookies off (it's a static Gatsby site, which does not
need and cannot use cookies).

For all his hard work, his site is a blank page.

(Here's why, if you care: [https://sneak.berlin/20200211/your-
website/](https://sneak.berlin/20200211/your-website/))

------
edhelas
Damn React is complicated. I added Dark mode in my app in a few lines of CSS
([https://github.com/movim/movim/blob/master/public/theme/css/...](https://github.com/movim/movim/blob/master/public/theme/css/color.css#L13))
and used the variables across the whole code. I just have to toggle a class on
the body and boom, it works.

Guys, you don't need all that JS, seriously. It's slow, it's useless most of
the time. And in the end you have to spend hours for something so simple.

Theming the colors of your website is the role of one tech: CSS. That's it,
you don't needs layers and layers of Javascript to change that.

Let's go back to server side rendering, a bit of HTML + CSS and JS and we're
done.

~~~
saagarjha
The author had a specific desire for a toggle on a static site, which requires
a bit of persistent state that CSS can't really capture. (FWIW, I don't care
for a toggle and match the system theme on my website as well. But dismissing
the author's work with your "few lines of CSS" fails to account for the fact
that this dark mode does something different than yours.)

~~~
memco
This doesn't capture all the state correctly even. When you first load the
page, if it is dark mode the XKCD loads a black on white (aka light) image. If
you toggle to light mode and back to dark the XKCD comic becomes a black on
white image. The state of image filters isn't consistent in the current
implementation.

~~~
saagarjha
Oh, I'm not claiming it does it correctly. (In fact, I have another comment
that points out another issue:
[https://news.ycombinator.com/item?id=22924571](https://news.ycombinator.com/item?id=22924571)).
But the claim that I was responding to made incorrect assumptions, and the
bugs in the implementation don't change that.

------
pcr910303
Seriously! Should every Gatsby blog post on HN have this useless 'React is
overkill for static sites' talk?

 __ __ _Gatsby is a static site generator!_ __ __

Running Gatsby does NOT make the whole website run on React. It makes a bunch
of HTML pages that are statically served - and some JS progressively enhances
it.

The complexity in this blog post has nothing to do with React, it's due to the
author's complex requirements that are very beneficial to the user.

Yes, the web is full of bloated pages. No, that's not the topic here. It's so
frustrating for people to complain about React on this great post - comment
about the post, people!

------
WiseWeasel
Perfect dark mode doesn’t have a toggle; it uses the prefers-color-scheme CSS
media query to key off your OS theme preference. At that point, there's no
problem with Gatsby or Next.

~~~
barrowclift
Not necessarily true, I'd argue the "perfect" dark mode uses the system's
theme by default like you said, but still allows visitors to manually set
light or dark should they wish to view the site a particular way (perhaps they
prefer the light mode, etc.)

~~~
asiachick
Agreed. S.O. recently added dark mode and for some reason my eyes couldn't
focus on it. No idea why. I run my editors and my terminal in dark themes.
Maybe they didn't have enough contrast. Maybe the fonts are too small or too
thin. I have my browser set to "prefer dark mode" but I'm really happy S.O.
let me opt out.

------
welcometomiami
Did anyone else happen to read this and think of
[https://en.wikipedia.org/wiki/Perfect_Dark](https://en.wikipedia.org/wiki/Perfect_Dark)?

~~~
sneilan1
Yes, I played that game thoroughly and I could not separate the project from
the game.

------
memco
The theme toggle button plays a sound when you click it. So not only does this
require state for the theme, but also the sound. Also curious is that clicking
the button to mute sound plays a sound.

I believe every OS defaults to silent actions for every button. Games, on the
other hand, often have sounds for hover/focus and click. I'm not sure what's
the better option, but if we're going to default to the OS for theme choice
shouldn't we also default to the OS for UI sound choice? I don't know if
there's any way to check that via CSS or JS. But I hope that if sites are
going to start implementing this more often that browsers at least expose an
API with a user preference if not the OS.

~~~
danappelxx
see: [https://www.joshwcomeau.com/react/announcing-use-sound-
react...](https://www.joshwcomeau.com/react/announcing-use-sound-react-hook/)

~~~
memco
Thanks! I did see this before. Didn't realize it was the same dev. They don't
address that Desktop OSes don't do sound for most things and also, I'm not
really sure I see sound used a lot in mobile apps, but maybe that's just the
apps I use? I think I would personally want more research and discussion on
this before I ventured down this path, but it is an interesting idea.

------
chrismorgan
I like my dark theme implementation which supports the same operations, but a
tad lighter, more flexible and not-Reacty: a toggle, falling back to the
prefers-color-scheme value if it hasn’t been toggled _or if JavaScript is
disabled_ (and I don’t think Josh’s does that), and avoiding a flash of
unstyled content. I wrote about it at [https://chrismorgan.info/blog/dark-
theme-implementation/](https://chrismorgan.info/blog/dark-theme-
implementation/).

The problem as a whole is definitely a tad fiddly, but I still think that
trying to do things in React is a substantial part of what’s making it
complicated here. I instead used regular stylesheets and the media attribute
to control the toggling, and I think that’s the best solution, because it’s
what makes it work perfectly with and without JavaScript. (I used external
stylesheets, but you could inline them if you wanted; the media attribute
works on <style> as well as <link>.)

Josh, I’d be interested in your opinion on it. It took me several iterations
to end up where I did (definitely didn’t occur to me to alter the media
attribute at first!), and I think it’d apply cleanly to such sites as yours,
and be better for the toggling.

------
darepublic
Dark mode shouldn't be harder with SSR apps, you would just send back the dark
mode CSS in the initial GET response. Complexity around this might be related
to trying to work with out of the box framework settings, but comparing this
problem to some advanced image recognition, while admittedly a joke.. is just
over-representing the problem imo. I'm sorry gatsby didn't give you an easy
way to do this, but if you understand the principles of web applications this
should not be an epic quest.

~~~
saagarjha
The problem is that the question of whether dark mode CSS should be applied is
only known once JavaScript can be executed on the page.

~~~
darepublic
That's the thing with SSR... (i.e. with Next) or with pre-rendering apps
before hand. In the initial GET request to the page, you also send a cookie
with user preference. Depending on their dark mode preference you can style
the page to be in dark mode as part of the server or pre-rendered markup -- no
JS required :o. Now.. how difficult that is to do with Gatsby I don't know.
But the fact that someone shot themselves in the foot with Gatsby, then
learned to hobble along on one foot isn't a cause for celebration imo.

~~~
saagarjha
I don't see how you could do that with a static site.

------
swlkr
Wow this website is really creative.

Not sure about the dark mode implementation, since it rendered a white screen
for me with no content on iOS, but on desktop it worked.

Also the general feel of the website is really nice, a lot of nice little
touches from the sounds to the "nonstop confetti party" when you sign up for
the newsletter.

~~~
noisem4ker
I'd rather stick with static web pages, thanks.

------
artursapek
We have had color schemes on Cryptowatch
([https://cryptowat.ch](https://cryptowat.ch)) for years, and since a year ago
we even let users create custom color schemes within the web app [1]

We use CSS variables for this. The stylesheet doesn't have any hard-coded
colors, it only references a set of several variables and many blends of these
variables. This way, you can let a user adjust a variable using a color picker
and the entire website adjusts dynamically as they do it. It also lets you
render any color scheme properly in your SSR (unlike the approach in this
article).

[1] [https://guides.cryptowat.ch/real-time-charting-
interface/cus...](https://guides.cryptowat.ch/real-time-charting-
interface/custom-theming)

~~~
bobbydreamer
Nice design

------
dgellow
How does the “shining stars effect” for the sentence “the perfect dark mode”
works? That’s really neat.

(I’m on mobile so cannot reverse)

~~~
mirthflat83
Looks like the author is generating span elements with random absolute
positioning

~~~
dgellow
Thanks!

------
mirthflat83
Wow. What a beautiful website!

------
animalgonzales
this is way too complicated and convoluted. just use global css selectors.

~~~
saagarjha
CSS selectors don't actually solve the problem the author had.

