
How to Use the CSS line-clamp Property to Truncate Text?
- Sajjad
- Typography
- 01 Oct, 2026
Your blog grid looks great in the design file, where every card has a two-line title and a three-line excerpt. Then real content arrives. One editor writes a seven-word headline, another writes twenty-two words, and the excerpts range from a single sentence to a full paragraph. The cards end up with wildly different heights, the "Read more" buttons no longer line up, and the grid looks broken. You need every excerpt to stop at exactly three lines and end with an ellipsis, regardless of how long the text is or how wide the card renders.
That is what line-clamp does. This article explains how it works, the exact CSS you need in 2026, how to build an accessible expand/collapse toggle, how to detect whether text was truncated, and the padding, flexbox, and accessibility pitfalls to watch for.
What Is line-clamp?
Line clamping limits a block of text to a specific number of lines and hides the rest, usually adding an ellipsis (…) at the end of the last visible line. Unlike a fixed max-height, a clamp is measured in lines, so it adapts automatically to font size, line height, and container width.
There are two related techniques:
| Technique | Lines | Key properties |
|---|---|---|
| Single-line truncation | 1 | white-space: nowrap, overflow: hidden, text-overflow: ellipsis |
| Multi-line clamping | 2+ | -webkit-line-clamp with display: -webkit-box |
Single-Line Truncation with text-overflow
For one line, you do not need line-clamp at all. The classic three-property pattern has worked in every browser for over a decade:
.truncate {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
This is ideal for file names, email subjects in a list, table cells, and breadcrumb items. It requires the element to have a constrained width, either explicitly or through its layout. Inside a flex container, add min-width: 0 to the flex item, because flex items default to min-width: auto and will refuse to shrink below their content width:
.file-row {
display: flex;
gap: 0.75rem;
}
.file-row .name {
flex: 1;
min-width: 0;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
Multi-Line Clamping: The Working CSS
For two or more lines, use the -webkit-line-clamp property. Despite the prefix, it is supported in Chrome, Edge, Safari, and Firefox, and the prefixed form is what the specification itself documents for compatibility. It only works in combination with two other legacy declarations:
.excerpt {
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 3;
overflow: hidden;
}
All four lines are required:
display: -webkit-boxswitches the element into the legacy flexible box model that the clamp depends on.-webkit-box-orient: verticalstacks content vertically.-webkit-line-clamp: 3sets the number of lines.overflow: hiddenhides everything after the clamp.
Leave out any one of them and the clamp silently fails. The ellipsis is added automatically; you do not need text-overflow.
What About the Unprefixed line-clamp?
The CSS Overflow Module Level 4 defines an unprefixed line-clamp shorthand that will eventually work without the legacy display value. Browser support for the unprefixed version is still incomplete, so ship the prefixed pattern and optionally add the standard property alongside it for the future:
.excerpt {
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 3;
line-clamp: 3;
overflow: hidden;
}
Browsers that do not understand line-clamp ignore it, and the prefixed declaration does the work.
Making the Clamp Reusable
Hard-coding a line count in each component gets repetitive. A custom property makes the clamp configurable per instance:
.clamp {
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: var(--clamp-lines, 3);
line-clamp: var(--clamp-lines, 3);
overflow: hidden;
}
<h3 class="clamp" style="--clamp-lines: 2">A very long article title that might wrap</h3>
<p class="clamp" style="--clamp-lines: 4">An excerpt that should stop after four lines...</p>
In Tailwind CSS v4
Line clamping is built into Tailwind v4 core. Use line-clamp-1 through line-clamp-6, arbitrary values like line-clamp-[8], and line-clamp-none to remove it. Single-line truncation is truncate.
type PostCardProps = { title: string; excerpt: string; href: string };
export function PostCard({ title, excerpt, href }: PostCardProps) {
return (
<article className="flex flex-col rounded-lg border p-6">
<h3 className="text-xl font-semibold line-clamp-2">
<a href={href}>{title}</a>
</h3>
<p className="mt-3 text-base line-clamp-3">{excerpt}</p>
<a href={href} className="mt-auto pt-4 font-medium">
Read more
</a>
</article>
);
}
Responsive variants work too: line-clamp-2 md:line-clamp-4 shows more text on wider screens.
The Padding Pitfall
The most common line-clamp bug is the "peeking" next line. If you put vertical padding on the clamped element, the hidden fourth line shows through inside the bottom padding, because overflow: hidden clips at the padding box, not the content box.
/* Buggy: part of line 4 is visible in the bottom padding */
.excerpt {
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 3;
overflow: hidden;
padding-block: 1rem;
}
The fix is to keep padding off the clamped element. Put it on a wrapper instead:
<div class="excerpt-wrapper">
<p class="excerpt">...</p>
</div>
.excerpt-wrapper {
padding-block: 1rem;
}
The same applies to borders and backgrounds that you expect to hug the visible text. Style the wrapper, clamp the text.
Other Gotchas
Clamp a Single Text Block
Line clamping is most reliable on one element containing inline content, such as a p or h3. If you clamp a container with several block children (multiple paragraphs, a heading and a paragraph), results vary between browsers and the ellipsis may not appear where you expect. Clamp the text element itself.
Inline Elements Inside the Clamp
Links, strong, and em inside the clamped text work fine. Inline images, badges, and icons can push line boxes taller and change where the clamp falls. Keep decorative inline elements outside the clamped element where possible.
Line Height Must Be Consistent
The clamp counts line boxes, not pixels. If the text mixes font sizes, lines have different heights and cards will no longer align. Use a consistent line-height on clamped elements. If you need to reserve space so short excerpts still occupy three lines, the lh unit gives you exactly one line height:
.excerpt {
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 3;
overflow: hidden;
line-height: 1.5;
min-height: 3lh; /* always occupy three lines, even with a short excerpt */
}
A max-height Fallback
If you ever need to support an environment without -webkit-line-clamp (some email clients, some embedded webviews), clip with max-height. You lose the ellipsis but keep the layout:
@supports not (-webkit-line-clamp: 3) {
.excerpt {
max-height: 3lh;
overflow: hidden;
}
}
Building an Accessible Read-More Toggle
Clamping only hides text visually. Screen readers still read the full content, which is usually what you want. But sighted users also need a way to see it. For expandable content such as reviews or descriptions, add a toggle button:
<div class="review">
<p id="review-42" class="clamp" style="--clamp-lines: 4">
The full review text goes here, however long it is...
</p>
<button type="button" class="toggle" aria-expanded="false" aria-controls="review-42" hidden>
Show more
</button>
</div>
.clamp.is-expanded {
-webkit-line-clamp: unset;
line-clamp: none;
}
document.querySelectorAll(".review").forEach((review) => {
const text = review.querySelector(".clamp");
const button = review.querySelector(".toggle");
// Only show the button if the text is actually truncated
const isTruncated = text.scrollHeight > text.clientHeight + 1;
if (!isTruncated) return;
button.hidden = false;
button.addEventListener("click", () => {
const expanded = text.classList.toggle("is-expanded");
button.setAttribute("aria-expanded", String(expanded));
button.textContent = expanded ? "Show less" : "Show more";
});
});
Key accessibility points:
- Use a real
button, not a link or a clickablespan, so it is focusable and keyboard-operable. - Set
aria-expandedso screen reader users know the state. - Hide the button when nothing is truncated. A "Show more" button that reveals nothing is confusing.
- Recheck on resize. Whether text is truncated depends on width. Use a
ResizeObserverif your layout changes significantly between breakpoints.
A React Version
"use client";
import { useLayoutEffect, useRef, useState } from "react";
export function ClampedText({ text, lines = 3 }: { text: string; lines?: number }) {
const ref = useRef<HTMLParagraphElement>(null);
const [expanded, setExpanded] = useState(false);
const [truncated, setTruncated] = useState(false);
useLayoutEffect(() => {
const el = ref.current;
if (!el) return;
const check = () => setTruncated(el.scrollHeight > el.clientHeight + 1);
check();
const observer = new ResizeObserver(check);
observer.observe(el);
return () => observer.disconnect();
}, [text]);
return (
<div>
<p ref={ref} className={expanded ? "" : "clamp"} style={{ ["--clamp-lines" as string]: lines }}>
{text}
</p>
{(truncated || expanded) && (
<button type="button" aria-expanded={expanded} onClick={() => setExpanded((v) => !v)}>
{expanded ? "Show less" : "Show more"}
</button>
)}
</div>
);
}
When Not to Clamp Text
Clamping is a layout convenience, not a content strategy. Avoid it when:
- The hidden text is essential. Prices, legal disclaimers, error messages, and form instructions must never be clamped.
- There is no way to reach the full text. A clamped excerpt is fine if the card links to the full article. A clamped product description with no toggle is not.
- Users zoom or change text spacing. WCAG 1.4.4 requires that text can be resized to 200% without loss of content, and 1.4.12 requires content to survive increased spacing (line height 1.5, paragraph spacing 2×, letter spacing 0.12em, word spacing 0.16em). Line-based clamping adapts better than a fixed
max-height, but it still hides more text as text grows, so make sure the full content is always reachable. - Headlines carry meaning. Truncating a headline mid-phrase can change what it says. Consider editing headlines to a sensible length instead, and use
text-wrap: balanceso short ones look good.
If you are designing for small screens specifically, the post on readable typography for mobile screens covers the broader sizing and spacing decisions, and how to make website typography accessible covers the WCAG requirements in more depth.
Clamping vs. Truncating on the Server
Sometimes the better answer is to shorten text before it reaches the browser, for example a fixed-length excerpt generated from the article body. Compare the approaches:
| Approach | Pros | Cons |
|---|---|---|
CSS line-clamp | Adapts to width and font size, no content changes | Full text still downloaded, visual only |
| Server-side character limit | Smaller HTML, predictable length | Does not adapt to layout, may cut mid-word |
| Editorial excerpt field | Best wording, written for the context | Requires authors to write it |
Many content sites use a combination: a frontmatter description or excerpt field as the source, and a CSS clamp as the safety net when the excerpt runs longer than the card allows.
line-clamp FAQ
Yes. Despite the prefix, it works in all current major browsers including Firefox, and the CSS Overflow specification documents it for web compatibility. Use it together with display set to -webkit-box, vertical box orientation, and hidden overflow.
Usually one of the required declarations is missing, the element has a display value from another rule overriding the -webkit-box value, or you are clamping a container with multiple block children instead of a single text element. Check the computed styles in DevTools to confirm all four declarations apply.
The clamped element has bottom padding, and overflow clipping happens at the padding edge, so the hidden line shows through. Move the padding to a wrapper element and keep the clamped element itself free of vertical padding.
Yes. Clamping hides text visually but leaves it in the accessibility tree, so screen readers announce the full content. That is usually desirable, but make sure sighted users can also reach the full text through a link or a show more button.
Not directly, because the number of lines is a discrete value. To animate expansion, transition max-height between a clamped height and a large value, or use a view transition. Keep the animation short and respect the reduced motion preference.
Compare the element's scrollHeight to its clientHeight. If scrollHeight is larger, content is hidden. Re-run the check when the element resizes, since truncation depends on the available width.
Conclusion
line-clamp is the cleanest way to keep cards, lists, and previews tidy when content length is unpredictable. The working recipe is four declarations: display: -webkit-box, -webkit-box-orient: vertical, -webkit-line-clamp, and overflow: hidden, with the unprefixed line-clamp added for the future. For a single line, use text-overflow: ellipsis and remember min-width: 0 in flex layouts.
The details are what separate a polished implementation from a buggy one. Keep padding off the clamped element, clamp a single text block, use consistent line heights, and add an accessible toggle whenever the full text is not available elsewhere. Never clamp content people need to complete a task.
Here are some useful references for going deeper on line clamping:
- MDN Web Docs: -webkit-line-clamp — syntax, required declarations, and browser compatibility.
- MDN Web Docs: text-overflow — single-line truncation with ellipsis.
- W3C: CSS Overflow Module Level 4 — the specification for line-clamp and its legacy prefixed form.
- Tailwind CSS: line-clamp — the built-in utilities in Tailwind v4.
- W3C WAI: Understanding SC 1.4.12 Text Spacing — why truncated text must survive increased spacing.


