blog

git clone https://git.ce9e.org/blog.git

commit
6a25589c7bea444479c681342c16823ead913f2c
parent
edbcc04e269fdd37dca8e9ef2473dfae79f1b00a
Author
Tobias Bengfort <tobias.bengfort@posteo.de>
Date
2024-03-15 13:12
add post on fluid typography

Diffstat

A _content/posts/2024-03-15-fluid-typography/demo/index.html 55 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
A _content/posts/2024-03-15-fluid-typography/demo/polyfill.js 11 +++++++++++
A _content/posts/2024-03-15-fluid-typography/demo/style.css 41 +++++++++++++++++++++++++++++++++++++++++
A _content/posts/2024-03-15-fluid-typography/index.md 227 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

4 files changed, 334 insertions, 0 deletions


diff --git a/_content/posts/2024-03-15-fluid-typography/demo/index.html b/_content/posts/2024-03-15-fluid-typography/demo/index.html

@@ -0,0 +1,55 @@
   -1     1 <!DOCTYPE html>
   -1     2 <html>
   -1     3 	<head>
   -1     4 	<meta charset="utf-8">
   -1     5 	<meta name="viewport" content="width=device-width, initial-scale=1">
   -1     6 	<title>Fluid Typography Demo</title>
   -1     7 	<link rel="stylesheet" href="style.css">
   -1     8 </head>
   -1     9 <body>
   -1    10 	<header>
   -1    11 		<h1>This is <em>still</em> a motherfucking website.</h1>
   -1    12 		<aside>And it's more fucking perfect than the last guy's.</aside>
   -1    13 	</header>
   -1    14 
   -1    15 	<h2>Seriously, it takes minimal fucking effort to improve this shit.</h2>
   -1    16 	<p><strong>7 fucking declarations.</strong></p>
   -1    17 	<p>That's how much CSS it took to turn that <a href="http://motherfuckingwebsite.com/">grotesque pile of shit</a> into this easy-to-read masterpiece. It's so fucking simple and it <em>still</em> has all the glory of the original perfect-ass website:</p>
   -1    18 	<ul>
   -1    19 		<li>Shit's <em>still</em> lightweight and loads fast</li>
   -1    20 		<li><em>Still</em> fits on all your shitty screens</li>
   -1    21 		<li><em>Still</em> looks the same in all your shitty browsers</li>
   -1    22 		<li>The motherfucker's <em>still</em> accessible to every asshole that visits your site</li>
   -1    23 		<li>Shit's <em>still</em> legible and gets your fucking point across</li>
   -1    24 	</ul>
   -1    25 
   -1    26 	<h3>And guess what, motherfucker:</h3>
   -1    27 	<p>You never knew it, but it's easy to improve readability on your site. Here's how.</p>
   -1    28 
   -1    29 	<h2>Let it breathe</h2>
   -1    30 	<p>Look at lines 1 and 2 of some shitty website you're building. Assuming they're not married they probably shouldn't be humping. The defaults are trash -- pick a minimum <code>line-height: 1.4</code> for body copy. Headings should be tighter. If you can't see that...piss off.</p>
   -1    31 	<p>If your text hits the side of the browser, fuck off forever. You ever see a book like that? Yes? What a shitty book.</p>
   -1    32 
   -1    33 	<h2>A little less contrast</h2>
   -1    34 	<p>Black on white? How often do you see that kind of contrast in real life? Tone it down a bit, asshole. I would've even made this site's background a nice <code>#EEEEEE</code> if I wasn't so focused on keeping declarations to a lean 7 fucking lines.</p>
   -1    35 
   -1    36 	<h2>Size Matters</h2>
   -1    37 	<p>I know your partner says otherwise, but it's true. Bump that body copy to render close to 16px or more. Smaller type works well for print, not the screen.</p>
   -1    38 
   -1    39 	<h2>Line-width, motherfucker</h2>
   -1    40 	<p>Looking at an LCD screen is strainful enough. Don't make me read a line of text that's 200 fucking characters long. Keep it to a nice 60-80 and users might actually read more than one sentence of your worthless dribble.</p>
   -1    41 
   -1    42 	<h3>Yes, this is <em>also</em> fucking satire, you fuck</h3>
   -1    43 	<p>I love what the creator of <a href="http://motherfuckingwebsite.com/">this site's inspiration</a> did. What I'm saying is that it's so, so simple to make sites easier to read. Websites are broken by default, they are functional, high-performing, and accessible, but they're also fucking ugly. You and all the other web designers out there need to make them not total shit.</p>
   -1    44 	<blockquote>
   -1    45 		"You're a fucking moron if you use default browser styles."
   -1    46 		<br> - Eleanor Roosevelt
   -1    47 	</blockquote>
   -1    48 	<hr>
   -1    49 
   -1    50 	<h2>Epilogue</h2>
   -1    51 	<p>Inspired by the geniuses behind <a href="http://motherfuckingwebsite.com/">motherfuckingwebsite.com</a> and <a href="http://txti.es">txti</a>.</p>
   -1    52 	<p>This page&mdash;that isn't a total fucking eyesore&mdash;was created by <a href="https://twitter.com/drew_mc">me</a> with help from <a href="https://twitter.com/gabehammersmith">him</a>.
   -1    53 
   -1    54 	<script src="polyfill.js" type="module"></script>
   -1    55 </html>

diff --git a/_content/posts/2024-03-15-fluid-typography/demo/polyfill.js b/_content/posts/2024-03-15-fluid-typography/demo/polyfill.js

@@ -0,0 +1,11 @@
   -1     1 var root = document.documentElement;
   -1     2 var styles = getComputedStyle(root);
   -1     3 
   -1     4 var setFactor = function() {
   -1     5 	var inlineSize = parseFloat(styles.inlineSize);
   -1     6 	var fontSize = parseFloat(styles.fontSize);
   -1     7 	root.style.setProperty('--screen-size', inlineSize / fontSize);
   -1     8 };
   -1     9 
   -1    10 window.addEventListener('resize', setFactor);
   -1    11 window.addEventListener('load', setFactor);

diff --git a/_content/posts/2024-03-15-fluid-typography/demo/style.css b/_content/posts/2024-03-15-fluid-typography/demo/style.css

@@ -0,0 +1,41 @@
   -1     1 :root {
   -1     2 	--max-inline-size: 40;
   -1     3 	--min-font-size: 0.8;
   -1     4 	--max-font-size: 1.2;
   -1     5 	--min-line-height: 1;
   -1     6 	--max-line-height: 1.6;
   -1     7 	--min-scale: 1;
   -1     8 	--max-scale: 1.6;
   -1     9 
   -1    10 	--screen-size: calc(100vi / 1rem);
   -1    11 	--factor: min(var(--screen-size) / var(--max-inline-size) / var(--max-font-size), 1);
   -1    12 	--font-size: 1;
   -1    13 
   -1    14 	--scale: calc(var(--min-scale) + (var(--max-scale) - var(--min-scale)) * var(--factor));
   -1    15 	--base-font-size: calc(var(--min-font-size) + (var(--max-font-size) - var(--min-font-size)) * var(--factor));
   -1    16 }
   -1    17 
   -1    18 body {
   -1    19 	max-inline-size: calc(var(--max-inline-size) * 1em);
   -1    20 	margin: 2.5em auto;
   -1    21 	padding: 0 1em;
   -1    22 }
   -1    23 
   -1    24 :root * {
   -1    25 	font-size: calc(var(--base-font-size) * var(--font-size) * 1rem);
   -1    26 	line-height: calc(var(--min-line-height) + (var(--max-line-height) - var(--min-line-height)) * var(--factor) / var(--font-size));
   -1    27 }
   -1    28 
   -1    29 h1 {
   -1    30 	--font-size: calc(var(--scale) * var(--scale));
   -1    31 }
   -1    32 
   -1    33 h2 {
   -1    34 	--font-size: var(--scale);
   -1    35 }
   -1    36 
   -1    37 @media (prefers-color-scheme: dark) {
   -1    38 	:root {
   -1    39 		color-scheme: dark;
   -1    40 	}
   -1    41 }

diff --git a/_content/posts/2024-03-15-fluid-typography/index.md b/_content/posts/2024-03-15-fluid-typography/index.md

@@ -0,0 +1,227 @@
   -1     1 ---
   -1     2 title: Fluid typography in 2024
   -1     3 date: 2024-03-15
   -1     4 tags: [design, css]
   -1     5 ---
   -1     6 
   -1     7 The basics of typography are quickly explained: Body text should have 60-80
   -1     8 characters per line, and line-height should be bigger if there are more
   -1     9 characters on a line.
   -1    10 
   -1    11 Usually in web design, font and font size is defined by user preferences (and
   -1    12 we should respect that!), so we are left with picking a line width and line
   -1    13 height, for example like this:
   -1    14 
   -1    15 ```css
   -1    16 body {
   -1    17   max-inline-size: 40em;
   -1    18   line-height: 1.6;
   -1    19 }
   -1    20 h1, h2 {
   -1    21   line-height: 1.2;
   -1    22 }
   -1    23 ```
   -1    24 
   -1    25 However, the line width you choose is only a maximum value. If the screen is to
   -1    26 small, lines can end up significantly shorter than you expected, and that can
   -1    27 throw off your composition.
   -1    28 
   -1    29 A simple approach would be to choose values that work reasonably well on both
   -1    30 small and large screens. But what fun would that be? No, let's go down this
   -1    31 rabbit hole!
   -1    32 
   -1    33 ## Fluid font size
   -1    34 
   -1    35 The first thing we could try is to shrink the font size so that the number of
   -1    36 characters per line does not decrease, or at least not as much. For example, we
   -1    37 could add something like this:
   -1    38 
   -1    39 ```css
   -1    40 body {
   -1    41   font-size: clamp(0.8em, 0.5em + 1vi, 1.2em);
   -1    42 }
   -1    43 ```
   -1    44 
   -1    45 *Note: If you use `em` in a font-size definition, it refers to the font-size of
   -1    46 the parent element.*
   -1    47 
   -1    48 This approach does of course have its limits, we cannot shrink the font
   -1    49 indefinitely. But it can provide some breathing room.
   -1    50 
   -1    51 However, we immediately are faced with a challenge: With the example above,
   -1    52 there are no guarantees that the font actually needs resizing. Maybe the users
   -1    53 already configured their font settings to match the screen, so we make it worse
   -1    54 for them instead of better.
   -1    55 
   -1    56 Imagine we had a custom property `--factor` that told us how much space we
   -1    57 have, going from `0` for no space at all to `1` for when we hit
   -1    58 `max-inline-size`. That would allow us to only scale the font size if needed:
   -1    59 
   -1    60 ```css
   -1    61 body {
   -1    62   font-size: calc(0.8em + 0.4em * var(--factor));
   -1    63 }
   -1    64 ```
   -1    65 
   -1    66 ## Fluid line height
   -1    67 
   -1    68 Long lines are hard to read because it is easy to jump to the previous line by
   -1    69 accident, reading the same text again and again. Increasing the line height can
   -1    70 help in those cases. [Tim Brown][molten-leading] already wrote about this idea
   -1    71 in 2012, but CSS has come a long way since:
   -1    72 
   -1    73 ```css
   -1    74 body {
   -1    75   line-height: calc(1 + var(--factor) * 0.6);
   -1    76 }
   -1    77 ```
   -1    78 
   -1    79 However, the line height should not so much depend on the line width in pixels,
   -1    80 but on the number of characters per line. That is why in the beginning I used a
   -1    81 smaller line-height for headings. So we have to throw the font-size into the
   -1    82 mix:
   -1    83 
   -1    84 ```css
   -1    85 body {
   -1    86   line-height: calc(1 + var(--factor) / 1em * 0.6);
   -1    87 }
   -1    88 ```
   -1    89 
   -1    90 Unfortunately, it's not that simple though. For one, it is currently not
   -1    91 possible to divide by values with units (more on that later). But even if that
   -1    92 were possible, that wouldn't help because the `em` would be evaluated on the
   -1    93 body element, not on the element where it is applied. So instead, we will add
   -1    94 yet another custom property:
   -1    95 
   -1    96 ```css
   -1    97 :root {
   -1    98   --font-size: 1;
   -1    99 }
   -1   100 :root * {
   -1   101   font-size: calc(var(--font-size) * 1rem);
   -1   102   line-height: calc(1 + var(--factor) / var(--font-size) * 0.6);
   -1   103 }
   -1   104 h1 {
   -1   105   --font-size: 2;
   -1   106 }
   -1   107 ```
   -1   108 
   -1   109 ## Fluid modular scale
   -1   110 
   -1   111 The relations between line length, font size, and line height we discussed so
   -1   112 far were all linear. However, some designers like to work with [modular
   -1   113 scales][modular-scale], i.e. power relations. For example:
   -1   114 
   -1   115 ```css
   -1   116 :root {
   -1   117   --scale: 1.5;
   -1   118 }
   -1   119 h2 {
   -1   120   --font-size: var(--scale);
   -1   121 }
   -1   122 h1 {
   -1   123   --font-size: calc(var(--scale) * var(--scale));
   -1   124 
   -1   125   /* see https://caniuse.com/mdn-css_types_pow */
   -1   126   --font-size: pow(var(--scale), 2);
   -1   127 }
   -1   128 ```
   -1   129 
   -1   130 Comig back to fluid typography, the people at [utopia][utopia] propose to also
   -1   131 adapt that base scale (they say this is what they do, but actually the do
   -1   132 linear interpolation between the powers):
   -1   133 
   -1   134 ```css
   -1   135 :root {
   -1   136   --scale: calc(1 + var(--factor) * 0.5);
   -1   137 }
   -1   138 ```
   -1   139 
   -1   140 ## The catch: calculating `--factor`
   -1   141 
   -1   142 So far we looked at relations between different typographic measurements. The
   -1   143 exact formulas and constants can be modified to match your personal taste.
   -1   144 However, one puzzle piece is still missing: the `--factor` property. And
   -1   145 unfortunately, that is not easy to come by. Spoilers: I don't have a perfect
   -1   146 solution for this yet.
   -1   147 
   -1   148 Ideally, we want something like this:
   -1   149 
   -1   150 ```css
   -1   151 :root {
   -1   152   --factor: min(100vi / 40em, 1);
   -1   153 }
   -1   154 ```
   -1   155 
   -1   156 As I mentioned before, [CSS Values and Units Module Level 3][css-values-3] does
   -1   157 not allow to multiply or divide by values with units. [Level 4][css-values-4]
   -1   158 will change that, but it is not yet implemented anywhere ([Firefox
   -1   159 ticket][bugzilla]).
   -1   160 
   -1   161 In the meantime, we can use JavaScript:
   -1   162 
   -1   163 ```js
   -1   164 var setFactor = function() {
   -1   165   var style = getComputedStyle(document.body);
   -1   166   var factor = parseFloat(style.inlineSize) / parseFloat(style.maxInlineSize);
   -1   167   document.documentElement.style.setProperty('--factor', factor);
   -1   168 };
   -1   169 
   -1   170 window.addEventListener('resize', setFactor);
   -1   171 window.addEventListener('load', setFactor);
   -1   172 ```
   -1   173 
   -1   174 But there is another issue: The maximum line width is defined as `40em`, which
   -1   175 depends on the current font size. The font size in turn depends on `--factor`,
   -1   176 which depends on the maximum line width. So there is a circular dependency.
   -1   177 
   -1   178 In practice, this circle can be broken because there is an absolute maximum
   -1   179 font size. In our case, that is `1.2 * 40 * 1rem`, were `rem` refers to the
   -1   180 user defined font size. In case you had wondered: That is why I never set
   -1   181 `font-size` on `:root`, because then it would have no longer been possible to
   -1   182 get that value.
   -1   183 
   -1   184 The rest is left as an exercise to the reader. If you are interested, I have
   -1   185 prepared a [complete demo](./demo/).
   -1   186 
   -1   187 ## Conclusion
   -1   188 
   -1   189 Fluid typography is an old idea, but still surprisingly hard to do. [CSS Values
   -1   190 and Units Level 4][css-values-4] will provide many improvements in this area,
   -1   191 some of which are already widely available (`min()`/`max()`/`clamp()`).
   -1   192 
   -1   193 Still, for everyday use these techniques are still too out-there. Using
   -1   194 JavaScript to tweak typography is a clear no-go for me. And the risk of having
   -1   195 weird cyclic dependencies is just too high.
   -1   196 
   -1   197 ## Bonus: some more ideas
   -1   198 
   -1   199 -	It could also make sense to adapt line-height to the x-height of the font.
   -1   200 	This could be achieved by combining the `em`, `ch`, and `ex` units. But it
   -1   201 	would again require division be values with units.
   -1   202 
   -1   203 -	[Container queries][container-queries] and the `cqi` unit could be useful to
   -1   204 	apply these techniques to individual components.
   -1   205 
   -1   206 -	Vertical rhythm is a very different approach to typography then what I
   -1   207 	described here: Instead of adjusting the line height to the font size, you
   -1   208 	start with a fixed line grid and fit the elements in there. That is
   -1   209 	especially useful for newspaper-style layouts where you want the lines in
   -1   210 	neighboring columns to match. I rarely see this on the web, but it could be
   -1   211 	interesting to bring these ideas together.
   -1   212 
   -1   213 - I tried to use [static interpolation][static-interpolation] (using paused
   -1   214 	animations to interpolate between values) for this, but found `calc()` easier
   -1   215 	to work with. Still, this is an interesting technique that could provide some
   -1   216 	benefits. [Scott Kellum][intrinsic-styles] has use this approach with some
   -1   217 	success.
   -1   218 
   -1   219 [molten-leading]: https://tbrown.org/notes/2012/02/03/molten-leading-or-fluid-line-height/
   -1   220 [modular-scale]: https://www.modularscale.com/
   -1   221 [utopia]: https://utopia.fyi/blog/css-modular-scales/
   -1   222 [css-values-3]: https://www.w3.org/TR/css-values-3/#calc-type-checking
   -1   223 [css-values-4]: https://www.w3.org/TR/css-values-3/#calc-type-checking
   -1   224 [bugzilla]: https://bugzilla.mozilla.org/show_bug.cgi?id=1827404
   -1   225 [container-queries]: https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_containment/Container_queries
   -1   226 [static-interpolation]: https://youtu.be/I_fBM1cEc_8?t=1383
   -1   227 [intrinsic-styles]: https://css-tricks.com/intrinsic-typography-is-the-future-of-styling-text-on-the-web/