{
  "$type": "site.standard.document",
  "content": {
    "$type": "site.standard.content.markdown",
    "text": "My wife and I were having an argument about line-height. It’s what happens when you live in a design systems house. She was working on updating some font properties and making a case for a larger line-height reflecting on the [WCAG guidelines 1.4.12 (Text spacing)](https://www.w3.org/WAI/WCAG22/Understanding/text-spacing.html). The guidelines were confusing so she also [found another reference within the United States Web Design System (USWDS)](https://designsystem.digital.gov/components/typography/#line-height) with the following quote:\n\n> **Longer texts require more line height.** Headings and other content elements no longer than a line or two can have a line height between 1 (`line-height 1`) and 1.35 (`line-height 3`). Longer texts should have a line height of at least 1.5 (`line-height 4`).\n\n**I was shooketh.** I’ve never seen any mention of having the length of the line affect the size of the `line-height` in the decades I’ve been working on the web. I’ve always seen it based on the `font-size`. It’s the reason why the popular recommendation has been to use a unitless value.\n\n```\n.some-text {\n    /** 150% the font-size */\n    line-height: 1.5;\n}\n```\n\nSo, I immediately set out to disambiguate this and I was shocked at what I found. Read on in the comments. Just kidding.\n\nI found four sources that all mention that the `line-height` should be affected by the length of a line; also known as the “measure”.\n\n-   [Pimp My Type](https://pimpmytype.com/line-length-line-height/) “Longer lines need more line height, shorter lines need less.”\n-   [Typographic Web Design](http://www.typographicwebdesign.com/setting-text/font-size-line-height-measure-alignment/) “Depending on the amount of text, the line length, the font size, and the size of a font’s x-height, you may find that the line height needs to be tightened or loosened slightly to promote comfortable reading.”\n-   [Google Fonts Knowledge](https://fonts.google.com/knowledge/using_type/choosing_a_suitable_line_height) “There’s no secret formula for setting the “right” line height for type. It’ll depend on the font, the size it’s set at, the length of the line, and the overall reading context”\n-   [Typographic Design Patterns And Best Practices](https://www.smashingmagazine.com/2009/08/typographic-design-survey-best-practices-from-the-best-blogs/) “Leading (or line height) will always depend on your chosen font size and measure (or line length).”\n\nIn fact, that last article published by Smashing Magazine has some data about the typography from popular blogs. Here’s a copy of the data:\n\n-   line height (pixels) ÷ body copy font size (pixels) = 1.48\n-   line length (pixels) ÷ line height (pixels) = 27.8\n-   space between paragraphs (pixels) ÷ line height (pixels) = 0.754\n\nThe article and study are from 2009. 16 years in the past from when this post is being written.\n\nThis got my wheels turning. While I know one of the articles said there’s no perfect formula, could we make something that has a general approximation to have a variable `line-height` based on both `font-size` and measure? In the year 2025, let’s see what we can do.\n\n## Minimums and maximums\n\nWe’ll want to use a `clamp()` function for a rate of change and we’ll want to know what the minimum and maximum `line-height` values will be. If we consider that paragraphs are typically the longest lines and the recommendation is supposed to be `1.5`, we’ll make that the maximum. Headlines are typically shorter in terms of line length and often have a `line-height` of between `1.1` and `1.2` so I’ll pick `1.15` for the minimum. We’ll set these as variables because we’ll need them later.\n\n```\n.text {\n    --lh-min: 1.15;\n    --lh-max: 1.5;\n    line-height: clamp(var(--lh-min), var(--lh), var(--lh-max));\n}\n```\n\nWhere `--lh` computes our `line-height` depending on the width of the text container. Based on what we have so far, we’re looking to generate a value between `1.15` and `1.5`.\n\nBefore we get into measuring the width, I also want to touch upon another rule of thumb. It’s usually a best practice for you to limit the number of characters for any line. This makes larger amounts of text easier to read because your eye doesn’t need to travel as far when scanning for the next line. The range of acceptable characters for this length is anywhere between 40 and 80, so I typically pick 60 characters as an average.\n\nWe’ll also want to pick a minimum number of characters. This is meant to denote the least amount of characters where the `line-height` stops decreasing if the lines are too short. For this, I’d like to determine what the longest *common* words are and count their characters. I don’t feel like accounting for “antidisestablishmentarianism” is realistic but accounting for “acknowledgements” is certainly reasonable. I couldn’t find a readily available list of these words since what common means is subjective. However, I did find a resource that lists long words of a certain number of syllables. I reviewed the lists and chose that [4 syllable words](https://www.syllablecount.com/syllables/longest/four_syllable_words) feel more common than [5 syllable words](https://www.syllablecount.com/syllables/longest/five_syllable_words). The average number of characters from the words listed is roughly 18, so we’ll use that as our minimum.\n\nNote that you can make your own logical determinations for the minimums and maximums of these numbers. It’s very likely that you might feel that the growing `line-height` begins too early at an 18 character width and opt to start growing later.\n\n## Measuring measure\n\nAdmittedly, it won’t be feasible with CSS alone to determine the length of each line of text, but we can get the largest width of an entire containing element of text. I’d argue that we actually don’t want each individual line to affect the `line-height` for each line differently. That would make the vertical rhythm of a single paragraph very erratic. Getting the width of a container element is more reasonable and uniform.\n\nAt first, I thought a container query would be the right direction. After all, we are trying to query the width of the text. However, there’s two problems with this. First, we’d need to query for every change that happens to the container, that would be like making a container query for every pixel and that isn’t realistic. Second, you can’t affect the container within the container query. This means we’d need some wrapping element as the container first, and then we could affect the text component in some way. However, that isn’t helpful since we really want to know the text component size. So how might we do this?\n\n## Time for a hack\n\nI found a post at Frontend Masters called “[How to Get the Width/Height of Any Element in Only CSS](https://frontendmasters.com/blog/how-to-get-the-width-height-of-any-element-in-only-css/)” which is precisely what we need to do. It uses a uninituitive trick with [scroll-driven animations](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_scroll-driven_animations) to achieve the effect. I highly recommend reading through the post if you want to learn some serious CSS magic. The tl;dr as I understand it is that as you resize an element, the amount allowed to scroll changes. That change will affect the scroll timeline and we use the amount if affects the timeline to determine the width. I could be wrong on that, I was just excited to have a working concept.\n\nFor now, we want to grab the baseline of the effect with a few modifications. Let’s start with the `@`\\-rules\n\n```\n@property --_x {\n  syntax: \"<number>\";\n  inherits: true;\n  initial-value: 0; \n}\n\n@property --ch {\n  syntax: \"<integer>\";\n  inherits: true;\n  initial-value: 0; \n}\n\n@keyframes x { to { --_x: 1} }\n```\n\nThis is mostly identical to what is in the post, however I’ve changed the `--w` variable to `--ch`. This will represent the number of characters or the width of the containing element in terms of `ch`. Now for the declaration block:\n\n```\n.text {\n  --lh-min: 1.15;\n  --lh-max: 1.5;\n  --ch: calc(1/(1 - var(--_x)));\n  display: inline-block;\n  overflow: auto;\n  timeline-scope: --cx;\n  animation: x linear;\n  animation-timeline: --cx;\n  animation-range: entry 100% exit 100%;\n  \n  line-height: clamp(\n    var(--lh-min),\n    var(--lh),\n    var(--lh-max)\n  );\n  \n  &::before {\n    content: '';\n    width: 1ch;\n    display: block;\n    view-timeline: --cx inline;\n  }\n}\n```\n\nA few things are changed here, other than removing references to the y-axis. First, I’ve removed the `position` properties because that can create new [stacking contexts](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_positioned_layout/Understanding_z-index/Stacking_context) and it doesn’t seem to change the final outcome. I’ve also set the `.text` to be `inline-block`. This is because block elements will just take up the width of their container. We want the amount of text to determine the size of the container. I’ve also updated `::before` to have `width: 1ch` which allows us to measure in terms of characters instead of pixels.\n\nNote that if this text element is in a flex container, you’ll want to set `align-self: start` here also so this flex child doesn’t stretch and act like a block element. I’d even recommend that you use flex containers because making all text elements behave as inline will cause weird wrapping for shorter text. Using flex will ensure elements stack as expected.\n\nNow we’re ready for the final part, math!\n\n## Equation of a line\n\nLike my previous post that explored some [advanced typographic tricks](/posts/two-typographic-tricks), we’re going to need to determine the rate of change here. We’re working linearly so we’ll need the same equations as before. First, the slope of the line.\n\nm\\=(y2−y1)/(x2−x1)m=(y\\_2-y\\_1)/(x\\_2-x\\_1)m\\=(y2​−y1​)/(x2​−x1​)\n\nWe can represent this in CSS like this:\n\n```\n.text {\n    --lh-min: 1.15;\n    --lh-max: 1.5;\n    --ch-min: 18;\n    --ch-max: 60;\n    --lh-delta: calc(var(--lh-max) - var(--lh-min));\n    --ch-delta: calc(var(--ch-max) - var(--ch-min));\n    --slope: calc(var(--ch-delta) / var(--lh-delta));\n}\n```\n\nWhere `--slope` is the result. Next, we need to solve for the “b” part of equation. This is what I’ll call the `--offset`.\n\nb\\=y1−(m×x1)b=y\\_1 - (m \\\\times x\\_1)b\\=y1​−(m×x1​)\n\nThat is represented in CSS like this:\n\n```\n.text {\n    --lh-min: 1.15;\n    --lh-max: 1.5;\n    --ch-min: 18;\n    --ch-max: 60;\n    --lh-delta: calc(var(--lh-max) - var(--lh-min));\n    --ch-delta: calc(var(--ch-max) - var(--ch-min));\n    --slope: calc(var(--ch-delta) / var(--lh-delta));\n    --offset: calc(var(--ch-min) - (var(--slope) * var(--lh-min)));\n}\n```\n\nAnd finally, we’ll use our restructured line equation to determine what the `line-height` value should be based on the `--ch` value.\n\nx\\=(y−b)/mx=(y-b)/mx\\=(y−b)/m\n\n```\n.text {\n    --lh-min: 1.15;\n    --lh-max: 1.5;\n    --ch-min: 18;\n    --ch-max: 60;\n    --lh-delta: calc(var(--lh-max) - var(--lh-min));\n    --ch-delta: calc(var(--ch-max) - var(--ch-min));\n    --slope: calc(var(--ch-delta) / var(--lh-delta));\n    --offset: calc(var(--ch-min) - (var(--slope) * var(--lh-min)));\n    --lh: calc((var(--ch) - var(--offset)) / var(--slope));\n}\n```\n\nPutting these variables together with the earlier CSS gives you the demo below.\n\n<p>See the Pen <a href=\"https://codepen.io/fauxserious/pen/VYwYjJx\"> CSS-Only, super easy screensize numeric pixel value width/height vars</a> by Donnie D’Amato (<a href=\"https://codepen.io/fauxserious\">@fauxserious</a>) on <a href=\"https://codepen.io\">CodePen</a>.</p>\n\nI’ve included a character counter at the end of the element so you can see the result of `--ch`. I’ve also made the element `contenteditable` so you can change the text right in the browser window. You should see the line height change with a `ch` value clamped between `18` and `60`.",
    "version": "1.0"
  },
  "description": "Changing what I thought I knew about typesetting.",
  "path": "/posts/relearning-line-height",
  "publishedAt": "2025-02-13T00:00:00.000Z",
  "site": "https://blog.damato.design",
  "tags": [
    "css",
    "typography"
  ],
  "textContent": "My wife and I were having an argument about line-height. It’s what happens when you live in a design systems house. She was working on updating some font properties and making a case for a larger line-height reflecting on the WCAG guidelines 1.4.12 (Text spacing). The guidelines were confusing so she also found another reference within the United States Web Design System (USWDS) with the following quote:\n\nLonger texts require more line height. Headings and other content elements no longer than a line or two can have a line height between 1 () and 1.35 (). Longer texts should have a line height of at least 1.5 ().\n\nI was shooketh. I’ve never seen any mention of having the length of the line affect the size of the  in the decades I’ve been working on the web. I’ve always seen it based on the . It’s the reason why the popular recommendation has been to use a unitless value.\n\nSo, I immediately set out to disambiguate this and I was shocked at what I found. Read on in the comments. Just kidding.\n\nI found four sources that all mention that the  should be affected by the length of a line; also known as the “measure”.\nPimp My Type “Longer lines need more line height, shorter lines need less.”\nTypographic Web Design “Depending on the amount of text, the line length, the font size, and the size of a font’s x-height, you may find that the line height needs to be tightened or loosened slightly to promote comfortable reading.”\nGoogle Fonts Knowledge “There’s no secret formula for setting the “right” line height for type. It’ll depend on the font, the size it’s set at, the length of the line, and the overall reading context”\nTypographic Design Patterns And Best Practices “Leading (or line height) will always depend on your chosen font size and measure (or line length).”\n\nIn fact, that last article published by Smashing Magazine has some data about the typography from popular blogs. Here’s a copy of the data:\nline height (pixels) ÷ body copy font size (pixels) = 1.48\nline length (pixels) ÷ line height (pixels) = 27.8\nspace between paragraphs (pixels) ÷ line height (pixels) = 0.754\n\nThe article and study are from 2009. 16 years in the past from when this post is being written.\n\nThis got my wheels turning. While I know one of the articles said there’s no perfect formula, could we make something that has a general approximation to have a variable  based on both  and measure? In the year 2025, let’s see what we can do.\n\nMinimums and maximums\n\nWe’ll want to use a  function for a rate of change and we’ll want to know what the minimum and maximum  values will be. If we consider that paragraphs are typically the longest lines and the recommendation is supposed to be , we’ll make that the maximum. Headlines are typically shorter in terms of line length and often have a  of between  and  so I’ll pick  for the minimum. We’ll set these as variables because we’ll need them later.\n\nWhere  computes our  depending on the width of the text container. Based on what we have so far, we’re looking to generate a value between  and .\n\nBefore we get into measuring the width, I also want to touch upon another rule of thumb. It’s usually a best practice for you to limit the number of characters for any line. This makes larger amounts of text easier to read because your eye doesn’t need to travel as far when scanning for the next line. The range of acceptable characters for this length is anywhere between 40 and 80, so I typically pick 60 characters as an average.\n\nWe’ll also want to pick a minimum number of characters. This is meant to denote the least amount of characters where the  stops decreasing if the lines are too short. For this, I’d like to determine what the longest common words are and count their characters. I don’t feel like accounting for “antidisestablishmentarianism” is realistic but accounting for “acknowledgements” is certainly reasonable. I couldn’t find a readily available list of these words since what common means is subjective. However, I did find a resource that lists long words of a certain number of syllables. I reviewed the lists and chose that 4 syllable words feel more common than 5 syllable words. The average number of characters from the words listed is roughly 18, so we’ll use that as our minimum.\n\nNote that you can make your own logical determinations for the minimums and maximums of these numbers. It’s very likely that you might feel that the growing  begins too early at an 18 character width and opt to start growing later.\n\nMeasuring measure\n\nAdmittedly, it won’t be feasible with CSS alone to determine the length of each line of text, but we can get the largest width of an entire containing element of text. I’d argue that we actually don’t want each individual line to affect the  for each line differently. That would make the vertical rhythm of a single paragraph very erratic. Getting the width of a container element is more reasonable and uniform.\n\nAt first, I thought a container query would be the right direction. After all, we are trying to query the width of the text. However, there’s two problems with this. First, we’d need to query for every change that happens to the container, that would be like making a container query for every pixel and that isn’t realistic. Second, you can’t affect the container within the container query. This means we’d need some wrapping element as the container first, and then we could affect the text component in some way. However, that isn’t helpful since we really want to know the text component size. So how might we do this?\n\nTime for a hack\n\nI found a post at Frontend Masters called “How to Get the Width/Height of Any Element in Only CSS” which is precisely what we need to do. It uses a uninituitive trick with scroll-driven animations to achieve the effect. I highly recommend reading through the post if you want to learn some serious CSS magic. The tl;dr as I understand it is that as you resize an element, the amount allowed to scroll changes. That change will affect the scroll timeline and we use the amount if affects the timeline to determine the width. I could be wrong on that, I was just excited to have a working concept.\n\nFor now, we want to grab the baseline of the effect with a few modifications. Let’s start with the \\-rules\n\nThis is mostly identical to what is in the post, however I’ve changed the  variable to . This will represent the number of characters or the width of the containing element in terms of . Now for the declaration block:\n\nA few things are changed here, other than removing references to the y-axis. First, I’ve removed the  properties because that can create new stacking contexts and it doesn’t seem to change the final outcome. I’ve also set the  to be . This is because block elements will just take up the width of their container. We want the amount of text to determine the size of the container. I’ve also updated  to have  which allows us to measure in terms of characters instead of pixels.\n\nNote that if this text element is in a flex container, you’ll want to set  here also so this flex child doesn’t stretch and act like a block element. I’d even recommend that you use flex containers because making all text elements behave as inline will cause weird wrapping for shorter text. Using flex will ensure elements stack as expected.\n\nNow we’re ready for the final part, math!\n\nEquation of a line\n\nLike my previous post that explored some advanced typographic tricks, we’re going to need to determine the rate of change here. We’re working linearly so we’ll need the same equations as before. First, the slope of the line.\n\nm\\=(y2−y1)/(x2−x1)m=(y\\2-y\\1)/(x\\2-x\\1)m\\=(y2​−y1​)/(x2​−x1​)\n\nWe can represent this in CSS like this:\n\nWhere  is the result. Next, we need to solve for the “b” part of equation. This is what I’ll call the .\n\nb\\=y1−(m×x1)b=y\\1 - (m \\\\times x\\1)b\\=y1​−(m×x1​)\n\nThat is represented in CSS like this:\n\nAnd finally, we’ll use our restructured line equation to determine what the  value should be based on the  value.\n\nx\\=(y−b)/mx=(y-b)/mx\\=(y−b)/m\n\nPutting these variables together with the earlier CSS gives you the demo below.\n\nSee the Pen  CSS-Only, super easy screensize numeric pixel value width/height vars by Donnie D’Amato (@fauxserious) on CodePen.\n\nI’ve included a character counter at the end of the element so you can see the result of . I’ve also made the element  so you can change the text right in the browser window. You should see the line height change with a  value clamped between  and .",
  "title": "Relearning line height",
  "updatedAt": "2026-06-10T01:51:47.430Z"
}