Limiting the amount of text in an element is a common web design requirement. You’ll often see an article with a three- or four-line excerpt with a button that lets you expand the full text.

You can create this design using a combination of CSS and JavaScript. But you can also do so using CSS alone. Discover two ways of limiting the text in a box and how you can create a dynamic expand button using just CSS.

The Webkit Technique

Create an empty folder and edit two files inside it: index.html and style.css. Inside the index.html file, create an HTML skeleton:

        <!DOCTYPE html>
<html lang="en">

<head>
  <title>Document</title>
  <link rel="stylesheet" href="style.css">
</head>

<body>
</body>

</html>

A link in the <head> section, to the style.css file, ensures that any CSS you add to that file will apply to this page. Next, paste the following HTML markup within the <body> tags in index.html:

        <section class="card-group">
  <article class="card">
    <h2>Article 1</h2>

    <p class="cuttoff-text">
      300-word text goes here
    </p>

    <input type="checkbox" class="expand-btn">
  </article>

  <article class="card">
    <h2>Article 2</h2>

    <p class="cuttoff-text">
      200-word text goes here
    </p>

    <input type="checkbox" class="expand-btn">
  </article>

  <article class="card">
    <h2>Article 1</h2>

    <p class="cuttoff-text">
      100-word text goes here
    </p>

    <input type="checkbox" class="expand-btn">
  </article>
</section>

The structure of this HTML is simple, yet it still uses semantic markup for accessibility. A section element contains three article elements. Each article comprises a heading, a paragraph, and an input element. You'll use CSS to style each article section into a card.

In the meantime, your page will look like this:

Images showing the articles section

From the image above, you can see varying lengths of text in each paragraph. 300 words in the first, 200 words in the second, and 100 in the third.

The next step is to start styling the page by adding CSS to the style.css file. Start by resetting the border on the document and giving the body a background color of white:

        *, *::before, *::after {
  box-sizing: border-box;
}

body {
  background: #f3f3f3;
  overflow: hidden;
}

Next, turn the element with the card-group class into a grid container with three columns. Each article section occupies a column:

        .card-group {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: .5rem;
  align-items: flex-start;
}

Style each article section as a card with white background color and a black, slightly round border:

        .card {
  background: white;
  padding: 1rem;
  border: 1px solid black;
  border-radius: .25em;
}

Finally, add some margins:

        h2, p {
  margin: 0;
}

h2 {
  margin-bottom: 1rem;
}

Save the file and check your browser. The page should look the page shown in the image below:

Image of grid container with three cards

The next step is to cut the number of lines for each text to 3. Here's the CSS for that:

        .cuttoff-text {
  --max-lines: 3;
  overflow: hidden;
   
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: var(--max-lines);
}

Start by setting a CSS variable, max-lines, to 3 and hiding overflowing content. Then set the display to -webkit-box and clamp the line to 3.

The following image shows the result. Each card displays an ellipse on the third line of text:

Screenshot of grid container with three cards limited to three text per line

This technique can be quite tricky to pull off. If you were to omit any property then everything is going to break. Another drawback is that you can't use a display property other than --webkit-box. For example, you might want to use Grid or Flexbox. For these reasons, the following technique is better.

A More Flexible Approach to Limiting the Number of Lines in Text

This technique allows you to do the same thing as the webkit approach, with the same results. But the big difference is that you have tons of flexibility because you're setting the height yourself. Plus you can use any display property or whatever styling option you prefer.

To get started, replace the CSS block for .cutoff-text with this:

        .cuttoff-text {
  --max-lines: 5;
  --line-height: 1.4;
  height: calc(var(--max-lines) * 1em * var(--line-height));
  line-height: var(--line-height);
  position: relative;
}

Setting a line height is important because you need to take it into account when determining your height. To get the height, you multiply the line height by your font size and the number of lines.

We add position:relative property because we need it to add the fade effect. Add the following CSS to complete the effect:

        .cuttoff-text::before {
  content: "";
  position: absolute;
  height: calc(2em * var(--line-height));
  width: 100%;
  bottom: 0;
  pointer-events: none;
  background: linear-gradient(to bottom, transparent, white);
}

The above CSS blurs the last line of text in each card. You can achieve a fading effect using the background property and a gradient. You must set pointer-events to none to ensure that the element is not selectable.

Here's the result:

Screenshot of grid container with card using blur effect

This technique achieved the same result as the previous one (plus the blur at the end). But you get more flexibility to use other kinds of layouts and designs.

Adding a Dynamic Expand and Collapse Button

Adding an expand/collapse button makes the cards more realistic and interactive. With this pattern, you expand the content by clicking the Expand button, after which the text changes to Collapse. So the button's text switches between "Expand" and "Collapse" as you click it. Furthermore, the rest of the content displays and hides in each respective state.

You've already defined an input element in your HTML. This element will serve as your button. To style this input into a button, include the following CSS in your style sheet:

        .expand-btn {
  appearance: none;
  border: 1px solid black;
  padding: .5em;
  border-radius: .25em;
  cursor: pointer;
  margin-top: 1rem;
}

When you hover on the button, you want to change the background color:

        .expand-btn:hover {
  background-color: #ccc;
}

Now the CSS for switching the text when the input is checked:

        .expand-btn::before {
  content: "Expand"
}

.expand-btn:checked::before {
  content: "Collapse"
}

Now when you click the button/input, the text goes from Expand to Collapse. But the content itself will not expand yet. To make it do so when you click the button, add the following CSS:

        .cuttoff-text:has(+ .expand-btn:checked) {
  height: auto;
}

This example uses the has() CSS selector to target the element. With this code, you're saying that if the cutoff text area has a checked expand button, the height of the card should be auto (which expands it).

Here's the result:

Screenshot showing a grid container with one expanded card and two collapsed ones

Other CSS Tips and Tricks to Learn

This article demonstrated two different methods for limiting the number of lines in a box using CSS. We even added a button for expanding/collapsing the content without writing a single line of JavaScript.

But there are tons of other tips and tricks in CSS. These tips offer you a creative way to achieve your desired layouts without hurting performance, readability, or accessibility.