- 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—that isn't a total fucking eyesore—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/