Crossbrowser CSS3 Bubble With Shadow

Here's what we are going to be doing in this tutorial:

There are a lot of ways to do message bubbles, so here are some key points to my technique:

  • completely image free;
  • arrow has smooth edges and shadow;
  • no javascript;
  • graceful degradation in IE7-8;
  • always positioned on the center of the source element no matter how wide it is or how much text is in the bubble.

I'm trying out a more detailed description of what's going on underneath the browser layouting engine in this tutorial.

Positioning

Let's start with the positioning stuff. First we need an element that will have a bubble attached to it:

<span class="bubble-source">
  Bubble that is always on
</span>

In order for bubble to work we need to be able to position stuff relative to that element. Any css position will do except for static. We'll use relative for the example:

.bubble-source {
  display: inline-block;
  position: relative;
}

I also made an element into inline-block so that it would be only as wide as the text inside.

Now we can start working on the bubble itself. Here's the full markup now:

<span class="bubble-source">
  Bubble that is always on
  <span class="bubble">
    <span class="bubble-content">Sample Text</span>
    <span class="bubble-arrow"></span>
  </span>
</span>

As far as positioning goes, the trick to centering lies in how browsers calculate percentage values for CSS left property. When the block is positioned absolutely 50% means 50% of the parent element. When the block is positioned relatively then calculations are done based on the width of element itself.

.bubble {
  position: absolute;
  left: 50%;
}

.bubble-content {
  position: relative;
  left: -50%;
}

This truck however has an unfortunate side effect of element trying to be as narrow as possible resulting in a line break pretty much after every word. To cancel such behavior we can either set minimum width for the .bubble-content or use white-space: nowrap to have all text on one line. I've opted in for latter for this example.

Positioning bubble on top is easy — setting bottom: 100% will do it, but the problem is that it will stick literally to the edge of the element, so to give us some room in-between bottom margin seems appropriate. We also need to make .bubble-content into a block-style element because of using <span> in the markup (which is an inline element by default).

Here's what we have now:

.bubble {
  position: absolute;
  bottom: 100%;
  left: 50%;
  margin: 0 0 10px 0;
}

.bubble-content {
  position: relative;
  left: -50%;
  white-space: nowrap;
  display: block;
}

You can see it in action too in this jsFiddle.

Styling Main Body

The rest of the styles for the .bubble-content aren't really interesting and are just presentational:

.bubble-content {
  position: relative;
  left: -50%;
  display: block;
  white-space: nowrap;

  padding: 5px 10px;
  color: #333;
  font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;
  font-size: 12px;
  text-shadow: 0 1px 0 rgba(255,255,255,0.5);

  background: #faead1;
  border-radius: 5px;
  -webkit-box-shadow: 0 0 6px 0 rgba(0,0,0,0.4);
  box-shadow: 0 0 6px 0 rgba(0,0,0,0.4);
}

Here's what we've got so far.

The Arrow

We all know that if we cut a rectangle in half on a diagonal we will get two triangles and that is exactly what we are going to do here.

First off all we need something that will the cutting and that will be .bubble-arrow itself:

.bubble-arrow {
  overflow: hidden;

  position: absolute;
  left: 0;
  margin: 0 0 0 -10px;
  top: 100%;
  width: 20px;
  height: 20px;
}

Here overflow:hidden hides everything that doesn't fit on the inside of the element.

As far as positioning goes, if we know width of the element we can absolutely position it in the center by using left: 50% same as in the bubble itself, but using negative left margin to push it back instead of relying on another element for that.

Now we need a rotated rectangle that will be cut in half. And that is where pseudo elements can help too since they are considered to be inside the base element.

So we carefully choose position and size, then rotate it 45 degrees and finally add necessary background and shadow to match main content of the bubble:

.bubble-arrow::after {
  content: '';
  position: absolute;
  left: 4px;
  top: -5px;
  display: block;
  width: 10px;
  height: 10px;

  -webkit-transform: rotate(45deg);
  -moz-transform: rotate(45deg);
  -o-transform: rotate(45deg);
  -ms-transform: rotate(45deg);
  transform: rotate(45deg);

  background-color: #faead1;
  -webkit-box-shadow: 0 0 5px 0 rgba(0,0,0,0.4);
  box-shadow: 0 0 5px 0 rgba(0,0,0,0.4);
}

That's it for modern browsers. Check it out for yourself.

Fixing Old Internet Explorers

I will be using a version of Paul Irish'es method with classes for html element to target IE7 and IE8.

Fix is actually pretty simple — we just going to render arrow with old border trick instead of rotation and clipping. We won't have shadows of course but it seems good enough for me.

.lte8 .bubble-arrow {
  width: 0;
  height: 0;
  border-top: 6px solid #faead1;
  border-left: 6px solid transparent;
  border-right: 6px solid transparent;
  margin: 0 0 0 -6px;
}
Social comments Cackle