Close Menu
    DevStackTipsDevStackTips
    • Home
    • News & Updates
      1. Tech & Work
      2. View All

      Akka introduces platform for distributed agentic AI

      July 14, 2025

      Design Patterns For AI Interfaces

      July 14, 2025

      Amazon launches spec-driven AI IDE, Kiro

      July 14, 2025

      This week in AI dev tools: Gemini API Batch Mode, Amazon SageMaker AI updates, and more (July 11, 2025)

      July 11, 2025

      ChatGPT falls for another Windows license key scam — generating valid codes in a guessing game after a researcher “gives up”

      July 14, 2025

      Germany wants Google and Apple to ban China’s “illegal” DeepSeek AI — after it failed to comply with data protection laws

      July 14, 2025

      Microsoft’s extra year of free Windows 10 security updates feels like a last-minute snooze button — while groups like “The Restart Project” still want to help users

      July 14, 2025

      The Xbox Ally and Xbox Ally X prices may have leaked — and if true, it’s not as bad as I thought

      July 14, 2025
    • Development
      1. Algorithms & Data Structures
      2. Artificial Intelligence
      3. Back-End Development
      4. Databases
      5. Front-End Development
      6. Libraries & Frameworks
      7. Machine Learning
      8. Security
      9. Software Engineering
      10. Tools & IDEs
      11. Web Design
      12. Web Development
      13. Web Security
      14. Programming Languages
        • PHP
        • JavaScript
      Featured

      The details of TC39’s last meeting

      July 14, 2025
      Recent

      The details of TC39’s last meeting

      July 14, 2025

      Modern async iteration in JavaScript with Array.fromAsync()

      July 14, 2025

      Vite vs Webpack: A Guide to Choosing the Right Bundler

      July 14, 2025
    • Operating Systems
      1. Windows
      2. Linux
      3. macOS
      Featured

      ChatGPT falls for another Windows license key scam — generating valid codes in a guessing game after a researcher “gives up”

      July 14, 2025
      Recent

      ChatGPT falls for another Windows license key scam — generating valid codes in a guessing game after a researcher “gives up”

      July 14, 2025

      Germany wants Google and Apple to ban China’s “illegal” DeepSeek AI — after it failed to comply with data protection laws

      July 14, 2025

      Microsoft’s extra year of free Windows 10 security updates feels like a last-minute snooze button — while groups like “The Restart Project” still want to help users

      July 14, 2025
    • Learning Resources
      • Books
      • Cheatsheets
      • Tutorials & Guides
    Home»News & Updates»Setting Line Length in CSS (and Fitting Text to a Container)

    Setting Line Length in CSS (and Fitting Text to a Container)

    July 14, 2025

    First, what is line length? Line length is the length of a container that holds a body of multi-line text. “Multi-line” is the key part here, because text becomes less readable if the beginning of a line of text is too far away from the end of the prior line of text. This causes users to reread lines by mistake, and generally get lost while reading.

    Luckily, the Web Content Accessibility Guidelines (WCAG) gives us a pretty hard rule to follow: no more than 80 characters on a line (40 if the language is Chinese, Japanese, or Korean), which is super easy to implement using character (ch) units:

    width: 80ch;

    The width of 1ch is equal to the width of the number 0 in your chosen font, so the exact width depends on the font.

    Setting the optimal line length

    Just because you’re allowed up to 80 characters on a line, it doesn’t mean that you have to aim for that number. A study by the Baymard Institute revealed that a line length of 50-75 characters is the optimal length — this takes into consideration that smaller line lengths mean more lines and, therefore, more opportunities for users to make reading mistakes.

    That being said, we also have responsive design to think about, so setting a minimum width (e.g., min-width: 50ch) isn’t a good idea because you’re unlikely to fit 50 characters on a line with, for example, a screen/window size that is 320 pixels wide. So, there’s a bit of nuance involved, and the best way to handle that is by combining the clamp() and min() functions:

    • clamp(): Set a fluid value that’s relative to a container using percentage, viewport, or container query units, but with minimum and maximum constraints.
    • min(): Set the smallest value from a list of comma-separated values.

    Let’s start with min(). One of the arguments is 93.75vw. Assuming that the container extends across the whole viewport, this’d equal 300px when the viewport width is 320px (allowing for 20px of spacing to be distributed as you see fit) and 1350px when the viewport width is 1440px. However, for as long as the other argument (50ch) is the smallest of the two values, that’s the value that min() will resolve to.

    min(93.75vw, 50ch);

    Next is clamp(), which accepts three arguments in the following order: the minimum, preferred, and maximum values. This is how we’ll set the line length.

    For the minimum, you’d plug in your min() function, which sets the 50ch line length but only conditionally. For the maximum, I suggest 75ch, as mentioned before. The preferred value is totally up to you — this will be the width of your container when not hitting the minimum or maximum.

    width: clamp(min(93.75vw, 50ch), 70vw, 75ch);

    In addition, you can use min(), max(), and calc() in any of those arguments to add further nuance.

    If the container feels too narrow, then the font-size might be too large. If it feels too wide, then the font-size might be too small.

    Fit text to container (with JavaScript)

    You know that design trend where text is made to fit the width of a container? Typically, to utilize as much of the available space as possible? You’ll often see it applied to headings on marketing pages and blog posts. Well, Chris wrote about it back in 2018, rounding up several ways to achieve the effect with JavaScript or jQuery, unfortunately with limitations. However, the ending reveals that you can just use SVG as long as you know the viewBox values, and I actually have a trick for getting them.

    Although it still requires 3-5 lines of JavaScript, it’s the shortest method I’ve found. It also slides into HTML and CSS perfectly, particularly since the SVG inherits many CSS properties (including the color, thanks to fill: currentColor):

    CodePen Embed Fallback
    <h1 class="container">
      <svg>
        <text>Fit text to container</text>
      </svg>
    </h1>
    h1.container {
      /* Container size */
      width: 100%;
    
      /* Type styles (<text> will inherit most of them) */
      font: 900 1em system-ui;
      color: hsl(43 74% 3%);
    
      text {
        /*
          We have to use fill: instead of color: here
          But we can use currentColor to inherit the color
        */
        fill: currentColor;
      }
    }
    /* Select all SVGs */
    const svg = document.querySelectorAll("svg");
    
    /* Loop all SVGs */
    svg.forEach(element => {
      /* Get bounding box of <text> element */
      const bbox = element.querySelector("text").getBBox();
      /* Apply bounding box values to SVG element as viewBox */
      element.setAttribute("viewBox", [bbox.x, bbox.y, bbox.width, bbox.height].join(" "));
    });

    Fit text to container (pure CSS)

    If you’re hell-bent on a pure-CSS method, you are in luck. However, despite the insane things that we can do with CSS these days, Roman Komarov’s fit-to-width hack is a bit complicated (albeit rather impressive). Here’s the gist of it:

    • The text is duplicated a couple of times (although hidden accessibly with aria-hidden and hidden literally with visibility: hidden) so that we can do math with the hidden ones, and then apply the result to the visible one.
    • Using container queries/container query units, the math involves dividing the inline size of the text by the inline size of the container to get a scaling factor, which we then use on the visible text’s font-size to make it grow or shrink.
    • To make the scaling factor unitless, we use the tan(atan2()) type-casting trick.
    • Certain custom properties must be registered using the @property at-rule (otherwise they don’t work as intended).
    • The final font-size value utilizes clamp() to set minimum and maximum font sizes, but these are optional.
    <span class="text-fit">
      <span>
        <span class="text-fit">
          <span><span>fit-to-width text</span></span>
          <span aria-hidden="true">fit-to-width text</span>
        </span>
      </span>
      <span aria-hidden="true">fit-to-width text</span>
    </span>
    .text-fit {
      display: flex;
      container-type: inline-size;
    
      --captured-length: initial;
      --support-sentinel: var(--captured-length, 9999px);
    
      & > [aria-hidden] {
        visibility: hidden;
      }
    
      & > :not([aria-hidden]) {
        flex-grow: 1;
        container-type: inline-size;
    
        --captured-length: 100cqi;
        --available-space: var(--captured-length);
    
        & > * {
          --support-sentinel: inherit;
          --captured-length: 100cqi;
          --ratio: tan(
            atan2(
              var(--available-space),
              var(--available-space) - var(--captured-length)
            )
          );
          --font-size: clamp(
            1em,
            1em * var(--ratio),
            var(--max-font-size, infinity * 1px) - var(--support-sentinel)
          );
          inline-size: var(--available-space);
    
          &:not(.text-fit) {
            display: block;
            font-size: var(--font-size);
    
            @container (inline-size > 0) {
              white-space: nowrap;
            }
          }
    
          /* Necessary for variable fonts that use optical sizing */
          &.text-fit {
            --captured-length2: var(--font-size);
            font-variation-settings: "opsz" tan(atan2(var(--captured-length2), 1px));
          }
        }
      }
    }
    
    @property --captured-length {
      syntax: "<length>";
      initial-value: 0px;
      inherits: true;
    }
    
    @property --captured-length2 {
      syntax: "<length>";
      initial-value: 0px;
      inherits: true;
    }
    CodePen Embed Fallback

    Watch for new text-grow/text-shrink properties

    To make fitting text to a container possible in just one line of CSS, a number of solutions have been discussed. The favored solution seems to be two new text-grow and text-shrink properties. Personally, I don’t think we need two different properties. In fact, I prefer the simpler alternative, font-size: fit-width, but since text-grow and text-shrink are already on the table (Chrome intends to prototype and you can track it), let’s take a look at how they could work.

    The first thing that you need to know is that, as proposed, the text-grow and text-shrink properties can apply to multiple lines of wrapped text within a container, and that’s huge because we can’t do that with my JavaScript technique or Roman’s CSS technique (where each line needs to have its own container).

    Both have the same syntax, and you’ll need to use both if you want to allow both growing and shrinking:

    text-grow: <fit-target> <fit-method>? <length>?;
    text-shrink: <fit-target> <fit-method>? <length>?;
    • <fit-target>
      • per-line: For text-grow, lines of text shorter than the container will grow to fit it. For text-shrink, lines of text longer than the container will shrink to fit it.
      • consistent: For text-grow, the shortest line will grow to fit the container while all other lines grow by the same scaling factor. For text-shrink, the longest line will shrink to fit the container while all other lines shrink by the same scaling factor.
    • <fit-method> (optional)
      • scale: Scale the glyphs instead of changing the font-size.
      • scale-inline: Scale the glyphs instead of changing the font-size, but only horizontally.
      • font-size: Grow or shrink the font size accordingly. (I don’t know what the default value would be, but I imagine this would be it.)
      • letter-spacing: The letter spacing will grow/shrink instead of the font-size.
    • <length> (optional): The maximum font size for text-grow or minimum font size for text-shrink.

    Again, I think I prefer the font-size: fit-width approach as this would grow and shrink all lines to fit the container in just one line of CSS. The above proposal does way more than I want it to, and there are already a number of roadblocks to overcome (many of which are accessibility-related). That’s just me, though, and I’d be curious to know your thoughts in the comments.

    Conclusion

    It’s easier to set line length with CSS now than it was a few years ago. Now we have character units, clamp() and min() (and max() and calc() if you wanted to throw those in too), and wacky things that we can do with SVGs and CSS to fit text to a container. It does look like text-grow and text-shrink (or an equivalent solution) are what we truly need though, at least in some scenarios.

    Until we get there, this is a good time to weigh-in, which you can do by adding your feedback, tests, and use-cases to the GitHub issue.


    Setting Line Length in CSS (and Fitting Text to a Container) originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

    Source: Read More 

    Facebook Twitter Reddit Email Copy Link
    Previous ArticleHello Robo’s Rebrand: Distilling Complex Tech Into Interfaces Anyone Can Use
    Next Article The art of cutting Test Maintenance in Half Welcome AI-Driven Performance Testing

    Related Posts

    News & Updates

    ChatGPT falls for another Windows license key scam — generating valid codes in a guessing game after a researcher “gives up”

    July 14, 2025
    News & Updates

    Germany wants Google and Apple to ban China’s “illegal” DeepSeek AI — after it failed to comply with data protection laws

    July 14, 2025
    Leave A Reply Cancel Reply

    For security, use of Google's reCAPTCHA service is required which is subject to the Google Privacy Policy and Terms of Use.

    Continue Reading

    How to Budget Smartly for Your First AI Project: A Step-by-Step Guide

    Web Development

    Gears of War: Reloaded remasters the game once again for Xbox, Game Pass, PC, and now, PS5 as well

    News & Updates

    Best Cheap Web Hosting in Bangladesh

    Web Development

    CVE-2025-47228 – Shell Injection Vulnerability in Netmake ScriptCase Production Environment Extension

    Common Vulnerabilities and Exposures (CVEs)

    Highlights

    zplug is a next-generation plugin manager for zsh

    June 3, 2025

    zplug has a good feature set including lazy loading, branch/tag/commit support, and many more. The…

    CVE-2025-48739 – TheHive SSRF

    May 23, 2025

    NVIDIA Nemotron Super 49B and Nano 8B reasoning models now available in Amazon Bedrock Marketplace and Amazon SageMaker JumpStart

    June 11, 2025

    North Korean Hackers Exploit GitHub and Dropbox in Targeted Spearphishing Attacks

    June 23, 2025
    © DevStackTips 2025. All rights reserved.
    • Contact
    • Privacy Policy

    Type above and press Enter to search. Press Esc to cancel.