Wednesday, October 03, 2012

CSS Ellipsis: how to manage multi-line ellipsis in pure CSS

CSS Ellipsis

Properly cutting off multi-line text is a surprisingly hard thing to do well.

  • overflow: hidden provides no indication of the text cut off.
  • text-overflow: ellipsis works only on single line content.
  • Various JavaScript solutions are fragile because they need explicit relayout whenever the box width or content changes.

We can do better with pure CSS and a little imagination.

Ellipsis animation_1

The image above is an animated GIF that shows the finished product of our CSS Ellipsis approach. Note how the ellipsis appears and disappears when the window is made narrower or wider. There is no JavaScript listening to window resize events or controlling ellipsis element visibility.

So what kind of magic is that?

CSS Ellipsis Implementation

The final CSS Ellipsis result demonstrated in the animated GIF above is gradually built from basic floats over 9 steps in the tutorial below. If you want to understand how the technique works, we recommend following the links and experimenting with code examples. This writeup will provide more of a high-level overview.

Obviously, cutting the text off is the easy part. The hard part is getting an element to appear in the corner of overflowing container then having it disappear if the container is not overflowing. Since doing this second feat is difficult, we will start with the opposite – placing an element in the corner of the container when the container is too short.

CSS Ellipsis_1

01-idea: Both text samples use fixed height "wrap<" elements, containing three floats inside. The first "prop" float is the measuring stick against which the content is compared. When the "main" content is shorter or equal in height to "prop", the "end" element can roam free in the bottom right corner. When the "main" is longer than "prop", the "end" element is trapped under "prop".

02-switchback: Now, let's flip the scenario and make this work properly by showing the ellipsis element when the content is too long for the container. For demonstration reasons, we will create a new "realend" element within "end". The location of "realend" is defined relative to that of "end", so that when "end" is trapped in the corner, "realend" lands on the bottom right and when "end" is free, "realend" is out of bounds.

.realend {
    position: absolute;
    width: 100%;
    top: -50px;
    left: 300px;

This is beginning to look like a proper text-overflow implementation, but all its plumbing is exposed and widths are fixed.

CSS Ellipsis_2

03-relative: We make no visual changes in this step. However, we eliminate "end" and place "realend" in the right spot through the use of position: relative instead of position: absolute. position: relative is perfect here because it shifts element for the purpose of rendering while maintaining its original place for the purpose of layout.

04-narrow: It would be cumbersome to resize "prop" every time the size of "realend" ellipsis content changes. Instead, we shrink "prop" and prevent it from wasting space through the use of negative margins. Note how the negative margin trick on "realend" allows it to fit into 5 pixels of "prop"-held space.

.realend {
    float: right; position: relative;
    top: -50px; left: 300px;
    width: 100px; margin-left: -100px;
    padding-right: 5px;

05-fluid: In this step we switch container widths to percentages to enable them to scale along with viewport. This is relatively easy to do because a negative margin on "realend" compensates for its width and thus allows use of left: 100%.

06-sweep: We replace our "prop" and "realend" elements with :before and :after generated content. This allows the markup structure to be simplified. Outer "wrap" element is renamed to "ellipsis".

07-overflow: We apply overflow to hide inner workings of the layout. In addition, the wrapper element is expanded from 50% to 100% because we are no longer trying to demonstrate placement of the "realend" element when content text gets too short. Content length is increased to compensate.

Ellipsis animation_2

08-complete: We change the content of the :after generated element to use an actual ellipsis and apply a transparent-to-white gradient background to hide any text it overlays. Debugging-specific properties, such as background highlighting and semi-transparency are now removed and we arrive at our final result.

09-readmore: We gild the lily a bit by adding "Read More" text that appears either as part of overflowed text or part of content, but not both. We also overlay all the text with a display: block link. This is better than wrapping content with a link because blocky elements are not reliably contained inside links.

CSS Ellipsis browser support

So far we have tested on Safari 5.0, IE 9 (must be in standards mode), Opera 12 and Firefox 15.

Older browsers will still work quite well, as the meat of the layout is in normal positioning, margin and padding properties. if your platform is older (e.g. Firefox 3.6, IE 8), you can use the method but redo the gradient as a standalone PNG image or DirectX filter.

Generated content may also be a point of contention for older browsers but can be replaced with empty elements.

CSS Ellipsis limitations

If you rely on a fade out effect, you will need a solid background under your text. You can also dodge this limitation by using a different appearance for the overflow indication element, such as a torn or folded corner or an inkblot.

Since you will be cutting off multiline text with overflow: hidden, you need to get your math right to avoid cutting the last line in half. Pay attention to your vertical grid and make sure that no matter how many lines your header(s) and paragraph(s) take, the last line lands in same spot at the bottom of overflowing container.

Download CSS Ellipsis

Download the zip file for CSS Ellipsis. The archive contains all the examples, as well as a Sass mixin version of code.

via Mobify