In this guide I would like to show you how to create tooltips with shadows that also have a correct shadow at the pointer. With tooltips, the pointers are often generated using the border trick. This trick makes the element look triangular, but in fact it is still a rectangle, so using box-shadow does not work with this solution.
Requirements for the tooltips
- As little HTML markup as possible
- Display of a pointer with correct shadow
- Any content possible (also pictures, etc.)
- Optionally a frame should be possible
Step 1: Tooltips without frame
Let’s get down to business! First of all, it is obvious to define a container element which receives the class tooltip. This element initially contains the (arbitrary) content of the tooltip.
As already mentioned, we cannot use the border trick if the tooltip is to have a shadow or glow in its entirety.
The alternative is a square rotated by 45°. We can create a square using a pseudo element and align its absolute center horizontally centered at the bottom of the tooltip. If you rotate the square with transform: rotate(45deg), two corner points are exactly on the edge of the tooltip container.
Limitation 1: The technique presented here only works for right-angled pointers.
There are now two problems.
Problem 1: Überlagerung des Zeigers
Ganz offensichtlich ist, dass der Inhalt vom Zeiger-Objekt überlagert wird. Dies ist relativ einfach gelöst, indem wir dem Tooltip einen inneren Container verpassen.
Der Trick ist nun, dass der Inhaltscontainer die Hintergrundfarbe des Tooltips sowie den Innenabstand (padding) erhält. Der tooltip-content wird relativ positioniert, damit die natürliche Stapelreihenfolge für die Überlagerung des Zeigers sorgt.
Problem 2: The “real” width of the pointer
In the CSS, the width of the square was specified as 50 pixels. But in fact the arrow is now about 71 pixels wide. This is due to the rotation of the square, because the optical width of the pointer corresponds to the diagonal of the square. The length can be calculated with the Pythagorean theorem. √(50px² + 50px²) = width of the diagonal can be transformed to √(2 × 50px²) and further to √(2) × 50px = 70.71067812…px.
The crooked value is not really the problem here, but rather the fact that the developer defines a value in the CSS that produces a different result. Formally the result is correct, only intuitively it is not. This becomes especially problematic when such code is touched again after months or years.
The desired behaviour would be that the pointer gets the width as it is specified.
The implementation can now be seen in the use of the SASS variables. I saved the root of 2 as a fixed value with enough decimal places. If you use Compass, you can also use the sqrt-function for an even more precise calculation. So the size is now assigned as a crooked value so that the diagonal gets the target width ($target-size).
Furthermore I created some configuration variables to build the SCSS according to the DRY principle. The tooltips without frame would be finished with this.
Step 2: Extend tooltip with frames
Limitation 2: The frames only work with opaque colors.
Fortunately, the box-shadow property lets you specify multiple shadows. This can be used to specify an additional shadow that is not blurred and thus draws a hard line around the tooltip.
Here it is important to consider that the tooltip does not get bigger because of the shadow and when using the technique you might have to pay attention to additional necessary distance. In addition, the second shadow must be adjusted if necessary, as it is overlaid by the first hard shadow.
If the frame has a light color as in the example, you can see another weakness of the quick solution. You have to look closely, but the frame of the tooltip is overlaid at the pointer with the shadow of the pointer square – not nice!
Because of the weaknesses of the quick solution, I now explain another solution. Here the inner container and the pointer get the frame as a real border – the size problem would be solved. This introduces the next problem, because now the frame is above the pointer.
To solve this problem, it is necessary that the contents in the tooltip-content are marked with HTML elements. This is because you also create a pointer (without a frame) at this element and position it over the frame.
To ensure that the endings are clean, the pointer must be positioned exactly above the white part of the pointer with frame. We will calculate the required bottom property exactly. First, we overlay the pointer completely (including the frame) – with the following property: bottom: -($size / 2) – $tooltip-border-width;
Then we only need the width of the rotated frame by which we move the overlay area upwards again. We can calculate this again with Pythagoras, since we know the thickness of the frame: $tooltip-border-width * $squareroot-of-two.
The rear part of our bottom property now looks like this: – $tooltip-border-width + ($tooltip-border-width * $squareroot-of-two), which we can easily convert to + $tooltip-border-width * ($squareroot-of-two – 1).
Browser compatibility and advantages
We only use two CSS3 properties, box-shadow and transform (productive with prefix), which are supported by all relevant browsers today. So this technique can be used without restrictions. Unless you have to support Opera Mini, in which case I feel sorry for you.
Finally, a modified example with a bit more content to show this intended possibility, too. Here you can now imagine that there are pictures or other interactive elements included.