summaryrefslogtreecommitdiff
path: root/source/blog/2022/website-adventures-basics/index.md
blob: 31e0fc3b20de9e7e703936aa43161f78f56df9f1 (plain)
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
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
---
title: "Adventures in making this website:<br>reviewing the basics"
date: 2022-11-20
layout: "blog"
toc: true
---

Published on 2022-11-20.

Making and managing this personal website has been an adventure.
In this series, I go over the technical challenges I've encountered
and philosophical decisions I've made,
and I review some of the tools I've used along the way.
After [part 1](/blog/2022/website-adventures-generators/),
this is part 2, with more coming soon.



After the previous article about static site generators,
let's take a step back: as you probably know if you're reading this,
a modern websites consists of three components:
1. [HyperText Markup Language](https://en.wikipedia.org/wiki/HTML) (HTML),
   an [Extensible Markup Language](https://en.wikipedia.org/wiki/XML) (XML)
   that describes the structure of the page,
   e.g. top navigation bar, footer, titles, paragraphs, etc.
   For arguably historic reasons,
   it does some basic styling too, like making text *italic*,
   and it provides basic interactive elements like buttons.
   In recent years, it's also been increasingly controlling
   the browser's interaction with external resources.
2. [Cascading Style Sheets](https://en.wikipedia.org/wiki/CSS) (CSS)
   that control the visual design of the page.
   CSS consists of rules referring to certain HTML elements,
   and set things like colours and fonts.
   Due to limitations of HTML,
   CSS also tells the browser how to handle the page's HTML structure,
   by specifying e.g. elements' widths and positions.
3. [JavaScript](https://en.wikipedia.org/wiki/JavaScript) (JS),
   or specifically its [ECMAScript](https://en.wikipedia.org/wiki/ECMAScript) (ES) standard,
   is a programming language that can do many things:
   it can interact with a "dynamic" copy
   of the HTML structure to e.g. add interactive animations,
   it can handle complex server-browser communications, etc....
   or it can mine cryptocurrencies behind your back.
   In principle, JS is used to do anything that HTML and CSS can't,
   but in practice some features of HTML/CSS are so obscure
   that many web developers use JS anyway out of ignorance, or for compatibility.

This website barely uses JS, so the rest of this article is just about HTML and CSS.
Let's start by asking ourselves if those technologies are any good...



## The good: HTML

Right-click on this page and select *"View Source"* (or something similar)
to see the HTML file where the magic happens
(if you're reading this on a mobile device, you'll just have to trust me when I say it's magic).
Looks like a mess, right?
Well, in my opinion, HTML (or really any XML) is a good fit for the web,
because its hierarchical structure is a natural way to describe UIs.

However, the kind of UI being described by HTML has changed a lot over the decades:
once upon a time it was just one big block of text,
now we have interactive apps.
The web has outgrown a lot of HTML's original features,
but at the same time HTML has kept up with the Internet's needs just fine,
thanks almost entirely to the `<div>` tag.

Sure, we could have some philosophical debates about HTML's design decisions.
Why is there a distinction between `<i>` and `<em>`?
What's the point of some of [HTML5](https://en.wikipedia.org/wiki/HTML5)'s
new semantic tags if I can just replace them with `<div>`s?
Don't the new media elements (e.g. `<picture>`)
maybe control the browser's behaviour a bit *too* explicitly?
And whose idea was [`<marquee>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/marquee) anyway?
But overall it holds up pretty well to scrutiny.

I've heard people suggest that instead of XML,
HTML should've used [S-expressions](https://en.wikipedia.org/wiki/S-expression).
Yes, I wish we lived in a world where HTML and JS were both replaced by a single Lisp!
That would be wonderful, but unfortunately *this* is the world we live in;
the sooner we accept it, the better.
And if I'm honest, I like how XML shows me the element name in the closing tag)))))).



## The bad: CSS

CSS, on the other hand, is a pain.
I want the following statement on my gravestone:
*"At least 33% of the CSS you write is for fixing the unexpected behaviour of the other 67%"*.
I came up with this rule of thumb a few years ago, and I don't think it's failed me yet,
although note that I include code churn under "writing CSS".
My site's [`main.css`](/code/prefetch-jekyll/tree/source/infra/css/main.css)
is quite simple, but I've obsessed over it for many hours,
trying to trim all the fat without breaking anything.

Although CSS is well-standardized,
browser compatibility appears to be a surprisingly big issue.
This why we have projects like
[`normalize.css`](https://github.com/necolas/normalize.css),
[`destyle.css`](https://github.com/nicolas-cusan/destyle.css),
and [`reseter.css`](https://github.com/resetercss/reseter.css),
which I learned about later than I should have.
See the vertical line under this page's header?
You wouldn't believe how many times its location surprised me
while testing different browsers and devices.
Sure, you can argue that using an `<hr>` isn't ideal here,
but isn't this its intended purpose?

The fundamental issue is that CSS is trying to handle two things at the same time:
*formatting* and *layout*.
Formatting refers to fonts, colours, text alignment, cursor icon, borders, etc.,
and is quite simple: it shouldn't be able to surprise you
and barely needs to adapt to its environment,
and can therefore easily be handled by CSS.

Layout, however, is a hard problem.
Defining the positions and sizes of elements on *your* device is doable,
but what about other devices?
Does your text fit in that box?
Where did that awkward line break come from?
Those elements were side by side,
so why are they suddenly under each other,
and partially overlapping the rest of the page?
Half of writing CSS is getting things to look good on phones;
it's a lot harder than you'd expect!
Is this a phone, or an awkwardly-shaped desktop window?
Why is my font size not honoured on mobile?
Are you sure that isn't a tablet?
Or what if they connect their phone to a monitor?
Wait, is that a mouse?

Nowadays, we have technology that makes layout easier,
like [flex](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Flexible_Box_Layout/Basic_Concepts_of_Flexbox)
and [grid](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Grid_Layout/Basic_Concepts_of_Grid_Layout) layouts,
but these are clearly afterthoughts.
The CSS language itself is very basic
(but still [Turing-complete](https://stackoverflow.com/a/5239256)),
resulting in quite repetitive code
with a lot of hard-coded values.
Some aspects are improved by e.g. [Sass](https://sass-lang.com/),
a "syntax wrapper" that helps avoid repetition.
But Sass doesn't address any fundamental issues,
and CSS' newer quality-of-life features
don't work well (or at all) on older browsers.



## The ugly: Microsoft

Ah, the subject of backwards compatibility, always a fun one.
When Microsoft released Windows 10 in July 2015,
they introduced the Edge browser and deprecated Internet Explorer (IE),
which in itself is just normal software churn.
The catch is that Windows 7 (then in extended support)
and Windows 8.1 (then still in mainstream support)
weren't getting Edge, and were thus stuck with IE version 11.0 forever.
In effect, they left many users without an up-to-date browser...
for 4 years, until they ported Edge to Windows 7 and 8.1 in June 2019.

Okay, some people are using old Windows versions,
but who uses IE anyway, am I right?
Isn't its only function to download other browsers' installers?
Well, as of writing, IE makes up about 0.8% of the global desktop market
([source](https://gs.statcounter.com/browser-market-share/desktop/worldwide)).
In fact, for my job I need to use a virtual environment with only IE
(although it fortunately isn't exposed to the Internet).
When Microsoft decided to ship a browser with their OS,
it became their responsibility to maintain it for the fast-evolving Internet.
Microsoft, if you're reading this,
think of all the revenue you missed out on
by not tracking many of your users' browsing for 4 years!
Don't you want to be Google?!

The reason I bring it up is that IE still casts a long shadow over web development,
due to its lack of support for many recent features in HTML, CSS and JS.
If you try to do something fancy, or just use modern best practices,
you need to ask yourself if you want to support IE.
If you choose yes, your implementation will probably look quite different.
Take a look at the ["Can I use?"](https://caniuse.com/) website
to check which browsers have which features;
often IE lags behind.




## Some tips and tricks

As this website gradually became more advanced,
I found some interesting tricks to optimize things or enable new kinds of content.
In this series' future posts, I will dive into some topics in great detail,
but here I'm putting anything that won't fit elsewhere.



### Automatic light/dark theme

Depending on how you've configured your browser or operating system
(pretty much the same thing nowadays),
this website should have either a light or dark background.
I implemented the default light colour scheme as follows in CSS:

```css
/* Light theme (default) */
:root {
	--b: #ededed; /* background color */
	--f: #121212; /* foreground color */
	--a: #0000ff; /* link color */
}
```

The prefix `--` is required for so-called *custom properties*,
which will act as variables containing the colours of the theme.
The dark theme automatically overwrites these variables:

```css
/* Dark theme */
@media only screen and (prefers-color-scheme: dark) {
:root {
	--b: #121212;
	--f: #ededed;
	--a: #ffff00;
}
}
```

Here [`@media`](https://developer.mozilla.org/en-US/docs/Web/CSS/@media)
is a *media query*, which tells CSS how to behave depending on the environment in which it's being interpreted.
The magic words here are `prefers-color-scheme: dark`,
such that, if your browser knows you like dark themes,
it will apply the corresponding CSS rules.

If you were wondering: `only screen` means that the rule only applies
when the website is shown on a screen, instead of being printed.
But actually, that isn't the reason it's there (I mean, who prints webpages?).
The real reason is backwards compatibility with older browsers (i.e. IE)
that don't understand `prefers-color-scheme` (but do know `screen`),
to prevent them from applying the rules underneath anyway.
Basically, an old browser will see `only` while it's looking for a media type like `screen`;
since `only` isn't a valid type, it decides "this media query doesn't apply to me",
and moves on, ignoring the rules in this block.

So, now that the variables `--b`, `--f`, and `--a` are set correctly,
we apply them using the function `var()` as follows...
assuming the browser even supports CSS variables like this:

```css
body {
	/* Light theme fallback (for IE, duh) */
	background: #ededed;
	color: #121212;
	/* Apply selected theme */
	background: var(--b);
	color: var(--f);
}
```

In modern browsers that do support `var()`,
the latter lines simply overwrite the earlier ones.

But what if there's some content on the website with hard-coded colours, like a logo?
Well, then things get a lot more complicated.
Either you rethink your design to allow the same image to be used in multiple themes,
or you serve different images.
The latter approach is becoming easier
thanks to the brand-new `Sec-CH-Prefers-Color-Scheme` HTTP Header,
which asks the server to send over dark-themed resources.
If you're interested, read [this article](https://web.dev/user-preference-media-features-headers/).

For me that's all too much work, and this is supposed to be a static website after all,
meaning there shouldn't be any fancy client-server negotiations.
Instead, I chose my colours such that the dark theme is simply the inverse of the light one:
if an image is suitable to invert in this way too,
I simply give it the `.darkinv` class and apply the following CSS:

```css
.darkinv {}
@media only screen and (prefers-color-scheme: dark) {
	.darkinv {filter: invert(100%);}
}
```

This is what I've done on the [front page](/).
Obviously, IE doesn't support the `filter` property.



### Collapsible content

A common thing you might want to create is collapsible content:
a box that shrinks or expands when the user clicks a toggle.
Fortunately, HTML can do this out-of-the-box
using the `<details>` and `<summary>` elements.
Great, no JS required!
These tags were only recently added to HTML,
so (can you see where this is going?) they're [unsupported in IE](https://caniuse.com/details).
A demonstration:

<details open style="border: 3px dotted; border-color: #121212; border-color: var(--f); padding: 0.25rem 0.5rem;">
<summary style="display: block; color: #0000ff; color: var(--a);">Click me, I dare you</summary>
<em>Here we can bully IE users, because they can't close this `&lt;details&gt;` box:
IE is old and slow, and its users are even older and slower,
and they're also smelly! Take that!</em>
</details>

So... time for JS? Well, fortunately, there's another way,
using just old-school HTML and CSS:

<div class="proof">
<input type="checkbox" class="proof" id="proof-example"/>
<label class="proof" for="proof-example">Click to show (IE welcome too)</label>
<div class="proof-hidden">
<label class="proof" for="proof-example">Click to hide.</label>
<br>
<em>This is a safe space where users of all browsers can rejoice together in peace.</em>
</div>
</div>

Pretty cool eh? This box is structured as follows,
using only `<div>`, `<input>` and `<label>`:
```html
{% raw %}<!-- Wrapper for applying CSS to the entire collapsible object -->
<div class="collapsible">
<!-- (Invisible) checkbox that controls the content's visibility -->
<input type="checkbox" class="collapsible" id="my-example"/>
<!-- (Visible) text label standing in for the checkbox -->
<label class="collapsible" for="my-example">Click to show</label>
<!-- Wrapper for what should be hidden when the checkbox isn't checked -->
<div class="collapsible-hidden">
<!-- Replacement text label for when the content is being shown -->
<label class="collapsible" for="my-example">Click to hide</label>{% endraw %}
<!-- The actual content that we want to show/hide -->
...
</div>
</div>
```
And then the magic happens in CSS, where `x + y` means
"if `<x>` is immediately followed by `<y>` in the HTML document,
then apply this rule to the `<y>` element":
```css
/* Hide the content by default */
.collapsible-hidden {display: none;}
/* Make the checkbox invisible */
input.collapsible {display: none;}
/* If it's checked, then show the content... */
input.collapsible:checked + label + .collapsible-hidden {display: block;}
/* ... and hide the original text label */
input.collapsible:checked + label {display: none;}
```

Compared to `<details>`, this even gives some extra flexibility.
For an example of a page using this technique,
click [here](/know/concept/boltzmann-equation/) and scroll down.