Friday, October 16, 2015

A pure CSS solution for multiline text truncation

CSS3 gave us the wonderful property, text-overflow, which can do things like create ellipsis and gracefully cut off words. However, text-overflow has a serious limitation: it only works on a single line of text.
.block-with-text {
    overflow: hidden;
    white-space: nowrap;
    text-overflow: ellipsis;
    // don’t forget that .block-with-text can’t be an inline element
}
A few days ago I had to truncate a multiline block of text. It’s a common problem, but I was a disappointed because we still don’t have a simple, cross-browser CSS solution for it. I tried a few ideas, but each time I found that the ‘…’ wasn’t quite right. Sometimes it appeared far from the end of the text or fell over to the next line.
Finally I found the ideal solution.

What are the options?

In order to fully understand why this pure CSS solution is so awesome, I want to run through a few of the alternatives and the problems with each.
If you’d like, you can jump ahead to check out a live example of the solution: codepen demo.

1. -Webkit-line-clamp property

I really like this for it’s simplicity, but unfortunately it is not cross browser (doesn’t work in Firefox and Internet Explorer). I hope that in future we will have a regular, non-vendor-prefixed CSS property.
To use -webkit-line-clamp, add the following to your CSS:
.block-with-text {
    overflow: hidden;
    display: -webkit-box;
    -webkit-line-clamp: 3;
    -webkit-box-orient: vertical;  
}

2. Text-overflow: -o-ellipsis-lastline

As of version 10.60, Opera added the ability to clip text on multi-line blocks. To be honest I have never tried this property, but it allows you to use -webkit-line-clamp.

3. Using JavaScript

Here’s a simple Javascript function for truncating text, but it has some serious drawbacks.
#block-with-text { 
    height: 3.6em; 
}

function ellipsizeTextBox(id) {
    var el = document.getElementById(id);
    var wordArray = el.innerHTML.split(' ');
    while(el.scrollHeight > el.offsetHeight) {
        wordArray.pop();
        el.innerHTML = wordArray.join(' ') + '...';
     }
}
ellipsizeTextBox(‘block-with-text);
Some of the drawbacks of this method include requiring the block of text to have a fixed height, not waiting for the font to load and the possibility that the ‘…’ will appear on the next line after the main text.

4. Truncate the text on the server

This is an example of truncating the text in PHP. This method works fine in limiting the text, but requires messing with the server-side just to deal with presentation, which is supposed to the be the job of CSS.

$text = 'your long long text';
$maxPos = 100;
if (strlen($text) > $maxPos)
{
    $lastPos = ($maxPos - 3) - strlen($text);
    $text = substr($text, 0, strrpos($text, ' ', $lastPos)) . '...';
}

A Simple, Pure CSS Solution

Clearly none of these options are perfect. I wanted an easier and more bulletproof way to handle this, so I found that we can truncate text using two carefully placed CSS pseudo elements.
Here’s the full CSS. We’ll walk through the code below.
/* styles for '...' */ 
.block-with-text {
  /* hide text if it more than N lines  */
  overflow: hidden;
  /* for set '...' in absolute position */
  position: relative; 
  /* use this value to count block height */
  line-height: 1.2em;
  /* max-height = line-height (1.2) * lines max number (3) */
  max-height: 3.6em; 
  /* fix problem when last visible word doesn't adjoin right side  */
  text-align: justify;  
  /* place for '...' */
  margin-right: -1em;
  padding-right: 1em;
}
/* create the ... */
.block-with-text:before {
  /* points in the end */
  content: '...';
  /* absolute position */
  position: absolute;
  /* set position to right bottom corner of block */
  right: 0;
  bottom: 0;
}
/* hide ... if we have text, which is less than or equal to max lines */
.block-with-text:after {
  /* points in the end */
  content: '';
  /* absolute position */
  position: absolute;
  /* set position to right bottom corner of text */
  right: 0;
  /* set width and height */
  width: 1em;
  height: 1em;
  margin-top: 0.2em;
  /* bg color = bg color under block */
  background: white;
}

How It Works

Let’s imagine that we need to contain a block of text to a max of 3 lines. In order to do so, we have to handle the following cases:

1. The text is more than 3 lines

more-3-lines
The pseudo element :before with ‘…’ is in the right corner
Here, the text is justified, so you won’t ever see this gap

2. The text is less than 3 lines

less-3-lines

3. The text is exactly 3 lines

3lines

Benefits

  • 1. Pure CSS
  • 2. Responsive
  • 3. No need to recalculate on resize or font’s load event
  • 4. Cross browser

A couple things to watch out for

Unfortunately this solution also has some drawbacks:
  • 1. We need to have a plain background color for covering up the ‘…’ if the text is less than the max number of lines.
  • 2. we need some space for ‘…’, and if the parent block has overflow: hidden or overflow: auto then we need to remove style margin-right: -1em;.
Also for the patient reader, I created an SCSS mixin to do this faster: codepen demo
/* mixin for multiline */
@mixin multiLineEllipsis($lineHeight: 1.2em, $lineCount: 1, $bgColor: white){
  overflow: hidden;
  position: relative;
  line-height: $lineHeight;
  max-height: $lineHeight * $lineCount; 
  text-align: justify;
  margin-right: -1em;
  padding-right: 1em;
  &:before {
    content: '...';
    position: absolute;
    right: 0;
    bottom: 0;
  }
  &:after {
    content: '';
    position: absolute;
    right: 0;
    width: 1em;
    height: 1em;
    margin-top: 0.2em;
    background: $bgColor;
  }
}

.block-with-text {
  @include multiLineEllipsis($lineHeight: 1.2em, $lineCount: 3, $bgColor: white);  
}
What do you think? Do you have another solution for multiline text truncation? Let me know in the comments.

Sunday, September 20, 2015

emotional branding

  • Either way, Gobe has written ten commandments of emotional branding.
  • First, from consumers to people. Consumers buy. People live.
  • Second, From product to experience.  Products fulfill people's needs. Experiences fulfill desires.
  • Third, from honesty to trust. Honesty is expected. Trust is engaging and intimate.
  • Four, from quality to preference.   Quality for the right price is a given today.   Preference creates the sale.
  • Number five, from notoriety to aspiration, being known does not mean that you are also loved.
  • Number six, from identity to personality.  Identity is recognition.  Personality is about character and charisma.
  • Number seven, from function to feel. The functionality of a product is about practical or superficial qualities only. Sensorial design is about experiences.
  • Number eight, from ubiquity to presence. Ubiquity is seen.  Emotional presence is felt.
  • Nine, from communication to dialog.  Communication is telling, dialog is sharing.
  • Number ten. From service to relationship.Service is selling, relationship is acknowledgement.  
  • Basically, people love brands, but brands don't love back.This book is an effort in evaluating how to change that. Again, very marketing and advertising heavy. But still relevant, when in the design process, in determining color and typographic systems.

Tuesday, September 1, 2015

how to add google commetz

How to add Google+ Comments to any Website
Similar to Facebook's comment system, Google has created an embeddable comment system that can be added to websites and blogs. In my opinion, it looks very nice and works quite well, as well as integrating into Google+. Only problem is, the comment system is only officially available for Blogger, and it's not obvious how to add it to your website.
Luckily, it's actually quite easy to add Google+ comments to your own website. All you have to do is embed this little HTML snippet in your page and a comment box will appear there.
<script src="https://apis.google.com/js/plusone.js"></script>
<div id="comments"></div>
<script>
gapi.comments.render('comments', {
    href: window.location,
    width: '624',
    first_party_property: 'BLOGGER',
    view_type: 'FILTERED_POSTMOD'
});
</script>
The first script is essential, as without it you'll be left with a blank box instead of comments. The width component in the call to gapi.comments.render is also configurable - you can use it to set the default width of the comment box. However, it's responsive so it'll fit in a smaller box if you want it to.
There is however a limitation with this method of creating a comment box - for some reason there is a minimum height of around 600px so you may be left with a bit of blank space underneath your comment box. AFAIK there's not a way to get around it but it isn't that big of a problem uneless you're putting comment boxes in the middle of pages. The issue goes away when the comment box grows because there are more comments inside.
Here's an example of a comment box on this website. Test it out!

Wednesday, July 1, 2015

Sunday, May 10, 2015

pixel perfect images

After feeling let down by both of these solutions, I decided to build my own little tool using nothing but HTML & CSS, and here it is:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
.designOverlay {
    position: absolute; left: 0; top: 0;
    width: 100%; height: 101%;
    text-align: center;
}
.designOverlay a {
    position: fixed;
    z-index: 6000;
    width: 40px; height: 40px;
}
.designOverlay img {
    position: relative;
    z-index: 5000;
    opacity: 0.3;
}
.overlay-off {
    left: 55px; top: 15px;
    background: rgba(113,19,19,0.5);
}
.overlay-on {
    left: 15px; top: 15px;
    background: rgba(19,112,20,0.5);
}
#no-overlay:target {
    display: none;
}
1
2
3
4
5
<div class="designOverlay">
    <a class="overlay-off" href="#no-overlay"></a>
    <img id="no-overlay" src="image.png" alt="" />
    <a class="overlay-on" href="#"></a>
</div>
This small amount of code allows you to insert your design onto the page on top of your HTML. It will be positioned in the centre of your browser automatically, no matter how wide it is, and will be at 30% opacity, which is my preffered level of opacity for this. Feel free to change any of this yourself. The great thing about this method is that you can customise it to get it exactly how you want it. It’s also incredible quick and easy to use, and the image will persist no matter how many times you refresh your browser. Simply add it to your pages and go. I add it to the header file that is then included on every page type to make it super easy to add and then remove later when it’s no longer needed.
The code will also place two buttons in the top-left corner of the browser that will allow you to switch the design on and off, using the :target CSS selector. This allows you to toggle the image on and off, and works by adding a tag to the address bar and then doing 2 different actions depending on whether the tag is there or not. In this case, it’s either showing the image or not showing it.
The container around the image is set to 101% of your browser’s height, to ensure that the scroll bar is visible when the image is turned off. This avoids the potential issue of your HTML jumping left and right based on whether the bar is there or not.