-
Notifications
You must be signed in to change notification settings - Fork 0
/
rss.xml
208 lines (188 loc) · 40.3 KB
/
rss.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Learnings and Code]]></title><description><![CDATA[Things that I enjoy playing with, processes that I keep coming back to and learnings that I feel like sharing.]]></description><link>https://joammartins.github.io</link><generator>GatsbyJS</generator><lastBuildDate>Mon, 31 May 2021 19:12:56 GMT</lastBuildDate><item><title><![CDATA[Gatsby, Lumen and Dark Mode]]></title><description><![CDATA[How I implemented dark mode in a Gatsby Lumen using CSS variables]]></description><link>https://joammartins.github.io/posts/gatsby-lumen-and-dark-mode</link><guid isPermaLink="false">https://joammartins.github.io/posts/gatsby-lumen-and-dark-mode</guid><pubDate>Mon, 31 May 2021 20:04:37 GMT</pubDate><content:encoded><h1 id="python-based-static-webpage" style="position:relative;"><a href="#python-based-static-webpage" aria-label="python based static webpage permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Python-based static webpage</h1>
<p>Previously, my web presence was composed of a static page built using staticjinja, an HTML5 UP template, and
my own json parsing logic to build the different elements displayed. Back when I made it I had little experience with
Javascript and no wish or need to update it continually as one does with a blog.<sup id="fnref-1"><a href="#fn-1" class="footnote-ref">1</a></sup> It looked something like this:</p>
<figure class="float-center">
<span class="gatsby-resp-image-wrapper" style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; ">
<span class="gatsby-resp-image-background-image" style="padding-bottom: 43.75%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAJCAYAAAAywQxIAAAACXBIWXMAAAsTAAALEwEAmpwYAAACFElEQVQoz02QSU8UURSF+2/YIthD9chsza+rq7EZFmgCLYmogDKI2AaX/DL3unWDCQnVzLBUIggyB8jnrWqnSk5u7nv3nvrOi1mO4oHl0tVroI+P405MYA8PY5V9rN5ejEwGvbUV/U4cPR7HaGvDyGYx5c6uVHAeP8J9/gz1eg71fpFYttiBliui5dvRX0zgTk9jj4xgVauYponq7sYrFPDSGl4qTUmT2tGBb1nYJYU9NIQzNiZ7r1D1OrF7iTRtSY1kJo8x9RI1P496Oo4SSscrU7Ft+np6qIjpw3yBSjZHRQyrcq5cF3egn1KthpqcRL2ZJxYapbKFiNCcmUW9q+NNTVGuPcEbGESFi52d+MUivsT3M1k8iexK7wmlJ0l8SVQKY8/OEEtoOdJimJbY+uwczuIilhiao6OY1X4MiW3IsplKYbS0YMTvRlVPJNCLBQzLlLkq1ugIjlBGhn8I3bd1+paW8BcWKMtlWf4cEpTCt2xvR8n7uWKkkkmUUJa6uvBch/LgoCSq4QtIFDmUli/y4eMntvf3Wd3bIwi1s0OwtUWwsUFjfZ3VICBoNAiCRrOurRHIebC5SbC9TWN3NzRsEqZzBZZXVgi/s6srLq6vubi5aer2lnPpD09O+Hl5GfV/dXP7b04UEd5PZwmjf17+wqUM/Tg95ej8nGPR0dlZVL8fH/P14IBvh4fNM9HR//o9/wsyEpdWAoOMJwAAAABJRU5ErkJggg=='); background-size: cover; display: block;"></span>
<img class="gatsby-resp-image-image" alt="Old website look" title="Old website look" src="/static/83dd1c750330984f5183481dc8b14661/7d769/old_website.png" srcset="/static/83dd1c750330984f5183481dc8b14661/5243c/old_website.png 240w,
/static/83dd1c750330984f5183481dc8b14661/ab158/old_website.png 480w,
/static/83dd1c750330984f5183481dc8b14661/7d769/old_website.png 960w,
/static/83dd1c750330984f5183481dc8b14661/87339/old_website.png 1440w,
/static/83dd1c750330984f5183481dc8b14661/f52e1/old_website.png 1831w" sizes="(max-width: 960px) 100vw, 960px" style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;" loading="lazy">
</span>
<figcaption>Old website look.</figcaption>
</figure>
<p>It worked well as I didn’t have the need to update it often, but perhaps leading to it being usually very out-of-date as
I changed projects and workplaces.
Adding to this, the advent — or should I say return? — of blogging and developer diaries made me want to start my own
blog.
This would be where I share what I learn along the way, some tricks and tips on Python and development strategies
that seem to work for me. </p>
<h1 id="javascript-static-site-generators" style="position:relative;"><a href="#javascript-static-site-generators" aria-label="javascript static site generators permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Javascript static site generators</h1>
<p>While Medium seems to be the platform of choice for these kinds of blogging, I’m a proponent of free open
source software and of learning-by-doing, so this was a good opportunity to learn some more Javascript, React and
Gatsby, an open source static site generator with good Markdown support.</p>
<h2 id="making-it-mine" style="position:relative;"><a href="#making-it-mine" aria-label="making it mine permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Making it mine</h2>
<p>I have always had an obsessive relationship with tinkering and making my digital experiences as close to my liking as
possible. A good example is <a href="https://darkreader.org/" target="_blank" rel="nofollow noopener noreferrer">Dark Reader</a>, an open source browser extension that <em>“inverts brightness
of web pages and aims to reduce eyestrain while you browse the web”</em>, which I currently use for handling pages that don’t
offer a native dark mode.</p>
<p>This website is based on <a href="https://github.com/alxshelepenok/gatsby-starter-lumen" target="_blank" rel="nofollow noopener noreferrer">Lumen</a>, a Gatsby starter blog with a
minimalistic and pleasing design. It does not support a dark mode out of the box, making its implementation an
interesting learning experience. It also seems to be a <a href="https://github.com/alxshelepenok/gatsby-starter-lumen/issues/688" target="_blank" rel="nofollow noopener noreferrer">request</a>
from people using the starter blog, so it’s a good opportunity to help out. </p>
<p>After some research — figuring out the quality of developer blog posts has become a skill in itself — I had a few good
ideas on how to do this. Ananya’s article<sup id="fnref-2"><a href="#fn-2" class="footnote-ref">2</a></sup> on dev.to was a great starting point, especially since most other articles
don’t suggest CSS custom properties (also called variables) for implementing dark mode. </p>
<p>The most common approach seems to be defining an alternative dark mode global <code class="language-text">&lt;body&gt;</code> class and using Javascript
before the body renders to edit its class to the dark mode explicitly. Since Lumen already makes use of Sass, I went
with a mixed approach, keeping Sass variables in the different components and assigning to them the CSS custom
properties. </p>
<p>This has its drawbacks too, as we can’t use Sass color module functions, but the very limited number of colors and page
types of the template allows for pre-setting all the colors we will use. It also has its benefits, since we can
directly use <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/Attribute_selectors" target="_blank" rel="nofollow noopener noreferrer">CSS attribute selectors</a> and
HMTL5’s <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/data-*" target="_blank" rel="nofollow noopener noreferrer">data-* global attributes</a>. We can
use an attribute selector dependent on our data-* attribute, changing the color variables depending on which <code class="language-text">theme</code>
data attribute we select.</p>
<h2 id="media-queries-and-setting-a-mode" style="position:relative;"><a href="#media-queries-and-setting-a-mode" aria-label="media queries and setting a mode permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Media queries and setting a mode</h2>
<blockquote>
<p>Media Queries allow authors to test and query values or features of the user agent or display device, independent of
the document being rendered. They are used in the CSS @media rule to conditionally apply styles to a document, and in
various other contexts and languages, such as HTML and JavaScript.</p>
<p>— <a href="https://drafts.csswg.org/mediaqueries-5/#prefers-color-scheme" target="_blank" rel="nofollow noopener noreferrer">Media Queries Level 5 specification</a></p>
</blockquote>
<p>We’re connecting another recent feature of CSS formatting, media queries and, more specifically, the <code class="language-text">prefers-color-scheme</code>
media query that informs the browser of the client’s OS dark/light mode preference. With this I can infer the reader’s
preference for a dark or light mode and use that preference to style the page accordingly. </p>
<div class="gatsby-highlight" data-language="javascript"><pre class="language-javascript"><code class="language-javascript"><span class="token keyword">const</span> mql <span class="token operator">=</span> window<span class="token punctuation">.</span><span class="token function">matchMedia</span><span class="token punctuation">(</span><span class="token string">'(prefers-color-scheme: dark)'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">const</span> hasMediaQueryPreference <span class="token operator">=</span> <span class="token keyword">typeof</span> mql<span class="token punctuation">.</span>matches <span class="token operator">===</span> <span class="token string">'boolean'</span><span class="token punctuation">;</span>
<span class="token keyword">if</span> <span class="token punctuation">(</span>hasMediaQueryPreference <span class="token operator">&amp;&amp;</span> mql<span class="token punctuation">.</span>matches <span class="token operator">===</span> <span class="token boolean">true</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
document<span class="token punctuation">.</span>documentElement<span class="token punctuation">.</span>dataset<span class="token punctuation">.</span>theme <span class="token operator">=</span> <span class="token string">'dark'</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span>
document<span class="token punctuation">.</span>documentElement<span class="token punctuation">.</span>dataset<span class="token punctuation">.</span>theme <span class="token operator">=</span> <span class="token string">'light'</span>
<span class="token punctuation">}</span></code></pre></div>
<p><code class="language-text">mql</code> should hold a boolean, indicating if the user’s OS color preference is dark mode. Like I mentioned before, I’m
using data-* attributes, which means that we can use the <code class="language-text">dataset</code> object of <code class="language-text">documentElement</code>. Subsequently, I set my
alternative CSS selector to use these data-* attribute:</p>
<div class="gatsby-highlight" data-language="sass"><pre class="language-sass"><code class="language-sass"><span class="token comment">// Colors, using css variables</span>
<span class="token property-line"><span class="token punctuation">:</span><span class="token property">root</span> {</span>
<span class="token comment">// Based on One Light: https://github.com/atom/one-light-syntax/blob/master/styles/colors.less</span>
<span class="token property-line"> <span class="token property">--bg-color</span><span class="token punctuation">:</span> rgb(231, 230, 223);</span>
<span class="token property-line"> <span class="token property">--base</span><span class="token punctuation">:</span> rgb(11, 23, 82);</span>
<span class="token property-line"> <span class="token property">--primary</span><span class="token punctuation">:</span> rgb(134, 69, 28);</span>
<span class="token property-line"> <span class="token property">--secondary</span><span class="token punctuation">:</span> rgba(11, 23, 82, 70<span class="token operator">%</span>);</span>
<span class="token property-line"> <span class="token property">--gray</span><span class="token punctuation">:</span> hsl(230, 23<span class="token operator">%</span>, 23<span class="token operator">%</span>);</span>
<span class="token property-line"> <span class="token property">--gray-border</span><span class="token punctuation">:</span> hsl(230, 77<span class="token operator">%</span>, 13<span class="token operator">%</span>);</span>
<span class="token selector">}</span>
<span class="token selector">[data-theme=</span><span class="token string">"dark"</span><span class="token selector">] {</span>
<span class="token comment">// Based on One Dark: https://github.com/atom/atom/blob/master/packages/one-dark-syntax/styles/colors.less</span>
<span class="token property-line"> <span class="token property">--bg-color</span><span class="token punctuation">:</span> hsl(220, 13<span class="token operator">%</span>, 18<span class="token operator">%</span>);</span>
<span class="token property-line"> <span class="token property">--base</span><span class="token punctuation">:</span> hsl(219, 14<span class="token operator">%</span>, 71<span class="token operator">%</span>); <span class="token operator">/</span><span class="token operator">/</span> mono-1</span>
<span class="token property-line"> <span class="token property">--primary</span><span class="token punctuation">:</span> hsl( 29, 54<span class="token operator">%</span>, 61<span class="token operator">%</span>); <span class="token operator">/</span><span class="token operator">/</span> orange-1</span>
<span class="token property-line"> <span class="token property">--secondary</span><span class="token punctuation">:</span> hsl(220, 9<span class="token operator">%</span>, 55<span class="token operator">%</span>); <span class="token operator">/</span><span class="token operator">/</span> mono-2</span>
<span class="token property-line"> <span class="token property">--gray-border</span><span class="token punctuation">:</span> hsl(220, 10<span class="token operator">%</span>, 40<span class="token operator">%</span>); <span class="token operator">/</span><span class="token operator">/</span> mono-3</span>
<span class="token property-line"> <span class="token property">--gray</span><span class="token punctuation">:</span> hsl(0, 0<span class="token operator">%</span>, 100<span class="token operator">%</span>); <span class="token operator">/</span><span class="token operator">/</span> white</span>
<span class="token selector">}</span></code></pre></div>
<p>As discussed before, depending on the root data attribute, the page will either display the default CSS colors, or the
dark mode colors.</p>
<h2 id="flash-of-unstyled-content" style="position:relative;"><a href="#flash-of-unstyled-content" aria-label="flash of unstyled content permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Flash of Unstyled Content</h2>
<p>The <a href="https://web.archive.org/web/20150513055019/http://www.bluerobot.com/web/css/fouc.asp/" target="_blank" rel="nofollow noopener noreferrer">flash of unstyled content (FOUC)</a>
is a fairly annoying consequence of sequential DOM building, where a browser will display
HTML without having fully loaded its CSS. It is especially noticeable on dark/light mode pages when the default flashing
page mode is different from the expected OS color mode, and getting around this issue is fairly easy with Gatsby.</p>
<p>In order to avoid this we’ll load and set the preferred media mode (dark or light) using Gatsby’s <code class="language-text">setPreBodyComponents</code>
function in its server side render options. When using this specific starter blog, editing the <code class="language-text">gatsby/on-render-body.js</code>
file is where these changes should be placed, since this file is referenced by the Gatsby server side rendering file, <code class="language-text">gastby-ssr.js</code>.</p>
<div class="gatsby-highlight" data-language="javascript"><pre class="language-javascript"><code class="language-javascript"><span class="token keyword">const</span> applyDarkModeFunc <span class="token operator">=</span> <span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">
(function() {
const mode = localStorage.getItem('theme');
if (mode !== null &amp;&amp; ['light', 'dark'].includes(mode)) {
document.documentElement.dataset.theme = mode;
return;
}
const mql = window.matchMedia('(prefers-color-scheme: dark)');
const hasMediaQueryPreference = typeof mql.matches === 'boolean';
if (hasMediaQueryPreference &amp;&amp; mql.matches === true) {
document.documentElement.dataset.theme = 'dark';
} else {
document.documentElement.dataset.theme = 'light'
}
})();
</span><span class="token template-punctuation string">`</span></span><span class="token punctuation">;</span>
<span class="token keyword">const</span> <span class="token function-variable function">onRenderBody</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token parameter"><span class="token punctuation">{</span> setPreBodyComponents <span class="token punctuation">}</span></span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
<span class="token function">setPreBodyComponents</span><span class="token punctuation">(</span><span class="token punctuation">[</span>
React<span class="token punctuation">.</span><span class="token function">createElement</span><span class="token punctuation">(</span><span class="token string">'script'</span><span class="token punctuation">,</span> <span class="token punctuation">{</span>
dangerouslySetInnerHTML<span class="token operator">:</span> <span class="token punctuation">{</span>
__html<span class="token operator">:</span> applyDarkModeFunc<span class="token punctuation">,</span>
<span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
<span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span></code></pre></div>
<p>As you can see, before drawing the page body we check for a stored preference in localStorage and return if there is one.
If not, we check the OS’s preferred color scheme and set te page theme accordingly.</p>
<h2 id="react-hooks-and-toggler" style="position:relative;"><a href="#react-hooks-and-toggler" aria-label="react hooks and toggler permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>React Hooks and Toggler</h2>
<p>Finally, we need to create a button to change between dark and light mode. For this we’re using React’s functional
programming hooks, making use of <code class="language-text">useState</code> and <code class="language-text">useEffect</code>. The first will be used to create a theme state variable and
setter. Using this theme variable, we’ll register it as a dependency of the useEffect-triggered function, which will be
run whenever the theme changes.</p>
<p>Whenever the button is pressed and the <code class="language-text">toggleTheme</code> function runs, the <code class="language-text">theme</code> variable is changed to the appropriate
new value by triggering our anonymous function:</p>
<div class="gatsby-highlight" data-language="javascript"><pre class="language-javascript"><code class="language-javascript"><span class="token keyword">import</span> React<span class="token punctuation">,</span> <span class="token punctuation">{</span> useState<span class="token punctuation">,</span> useEffect <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'react'</span><span class="token punctuation">;</span>
<span class="token keyword">import</span> styles <span class="token keyword">from</span> <span class="token string">'./DarkModeToggler.module.scss'</span><span class="token punctuation">;</span>
<span class="token keyword">function</span> <span class="token function">ThemeToggler</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token keyword">const</span> initTheme <span class="token operator">=</span> document<span class="token punctuation">.</span>documentElement<span class="token punctuation">.</span>dataset<span class="token punctuation">.</span>theme<span class="token punctuation">;</span>
<span class="token keyword">const</span> <span class="token punctuation">[</span>theme<span class="token punctuation">,</span> setTheme<span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token function">useState</span><span class="token punctuation">(</span>initTheme<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token function">useEffect</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
localStorage<span class="token punctuation">.</span><span class="token function">setItem</span><span class="token punctuation">(</span><span class="token string">'theme'</span><span class="token punctuation">,</span> theme<span class="token punctuation">)</span><span class="token punctuation">;</span>
document<span class="token punctuation">.</span>documentElement<span class="token punctuation">.</span>dataset<span class="token punctuation">.</span>theme <span class="token operator">=</span> theme<span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token punctuation">[</span>theme<span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">function</span> <span class="token function">toggleTheme</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token keyword">const</span> newTheme <span class="token operator">=</span> theme <span class="token operator">===</span> <span class="token string">'dark'</span> <span class="token operator">?</span> <span class="token string">'light'</span> <span class="token operator">:</span> <span class="token string">'dark'</span><span class="token punctuation">;</span>
<span class="token function">setTheme</span><span class="token punctuation">(</span>newTheme<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token keyword">return</span> <span class="token punctuation">(</span>
<span class="token operator">&lt;</span>div className<span class="token operator">=</span><span class="token punctuation">{</span>styles<span class="token punctuation">[</span><span class="token string">'toggler'</span><span class="token punctuation">]</span><span class="token punctuation">}</span><span class="token operator">></span>
<span class="token operator">&lt;</span>label<span class="token operator">></span>
<span class="token operator">&lt;</span>input
type<span class="token operator">=</span><span class="token string">"checkbox"</span>
onClick<span class="token operator">=</span><span class="token punctuation">{</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token function">toggleTheme</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">}</span>
hidden<span class="token operator">=</span><span class="token punctuation">{</span><span class="token boolean">true</span><span class="token punctuation">}</span>
<span class="token operator">/</span><span class="token operator">></span><span class="token punctuation">{</span><span class="token template-string"><span class="token template-punctuation string">`</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>theme<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string"> mode</span><span class="token template-punctuation string">`</span></span><span class="token punctuation">}</span>
<span class="token operator">&lt;</span><span class="token operator">/</span>label<span class="token operator">></span>
<span class="token operator">&lt;</span><span class="token operator">/</span>div<span class="token operator">></span>
<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token keyword">export</span> <span class="token keyword">default</span> ThemeToggler<span class="token punctuation">;</span></code></pre></div>
<h2 id="finishing-steps" style="position:relative;"><a href="#finishing-steps" aria-label="finishing steps permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Finishing steps</h2>
<p>With all of this in place we are pretty much done! Small details like the medium-style zoom provided by
<code class="language-text">gatsby-remark-images-medium-zoom</code> can be configured in the <code class="language-text">gatsby-config.js</code> page. I’d recommend using a CSS attribute
(as a string) as the backgroung option, which will make it work with the dark/light mode settings too!</p>
<p>This is all I needed to do to make this work, feel free to replicate and modify it if you’re looking for the same
functionality. I don’t have any comments section on this blog (on purpose), so feel free to open an issue on this page’s
repo or message me through LinkedIn.</p>
<h3 id="post-script-1" style="position:relative;"><a href="#post-script-1" aria-label="post script 1 permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Post Script 1</h3>
<p>While building and deploying I found a issue with SSR and the React component I built. The issue is that the browser DOM
methods aren’t available when building on server side, but following
<a href="https://www.gatsbyjs.com/docs/using-client-side-only-packages/#workaround-4-use-reactlazy-and-suspense-on-client-side-only" target="_blank" rel="nofollow noopener noreferrer">Gatsby’s documentation</a>
we can work around the problem. To not have to add another dependency I decided to use React component lazy loading
and check for the availability of the DOM methods, following the workaround 4 in the linked page.</p>
<p>These changes were introduced in the component that loads that offending component, <code class="language-text">Sidebar.js</code>, and building now works
correctly, as well as in development mode.</p>
<div class="footnotes">
<hr/><strong>Footnotes:</strong>
<ol >
<li class="footnote-list-item" id="fn-1" >
<p class="footnote-paragraph" style="display:inline; ">The code is still available under the <a href="https://github.com/joaommartins/joaommartins.github.io/tree/gh-page-staticjinja-based">gh-page-staticjinja-based</a> branch backing this repo.</p>
<a href="#fnref-1" class="footnote-backref" style="display:inline;text-decoration: none;">
^
</a>
</li>
<li class="footnote-list-item" id="fn-2" >
<p class="footnote-paragraph" style="display:inline; "><a href="https://dev.to/ananyaneogi/create-a-dark-light-mode-switch-with-css-variables-34l8">Article</a> by Ananya Neogi.</p>
<a href="#fnref-2" class="footnote-backref" style="display:inline;text-decoration: none;">
^
</a>
</li>
</ol></div></content:encoded></item></channel></rss>