Sass Tooltip Mixin

Tooltips are awesome. They can be a fun and helpful way to add context for visual elements on a page and, if executed well, create a subtle and delightful user experience. We’re going to create a SASS mixin in this post to make it easier to add tooltips on a project.

Before we get started, it’s worth nothing that hover effects in general are a bit of a sticky subject in web design. Sure, hover is great for the traditional mouse and keyboard setup, but what about touch devices where hover cannot be detected? I like what Jenn Lukas has to say:

Knowing your content and audience can help you decide if an intriguing experience is appropriate for your site; if it is, then hover responses can be a real asset.

The moral of the story? Consider whether hover effects add or detract from the experience of your site. Every site is different and there is no clear-cut answer other than considering whether adding something like tooltips enhances the content.

OK, that’s out of the way. Let’s make a SASS tooltip mixin.

Set up the Variables

We’re going to use a total of six variables in our mixin. I like to use SASS variables anytime we’re going to use the same value over and over again in the code or if we want to easily rename something, so we can update it later if needed.

// Variables
$prefix:          tooltip;
$contentAttr:     data-tooltip;
$trigger:         data-trigger;
$placement:       data-placement;
$black:           #000;
$white:           #fff;
  • $prefix: This will act as our class name
  • $contentAttr: This is the attribute we will target in our :after pseudo-element
  • $data-trigger: This will help us target hover and focus states
  • $placement: We will use this to set whether the tooltip is on the top, bottom, left or right

The last two are just the colors that will be used in this example.

Define, Hide and Style Our Tooltips

// The main tooltip class
.#{$prefix} {
  outline: none;
  position: relative;
	
  // Hide the tooltip when not active
  &:after,
  &:before {
    display: none\9; //:after:hover doesn't work with ie8
    font-size: 1rem;
    opacity: 0;
    outline: none;
    position: absolute;
    text-decoration: none;
    transition: opacity .3s linear 0s;
    visibility: hidden;
  }

  // The actual tooltip styles
  &:after {
    content: attr(#{$contentAttr});
    background-color: $black;
    color: $white;
    padding: 6px;
    white-space: nowrap;
  }

}

What did we do? We defined the classname for our tooltip using the $prefix variable. I’m going to call this “tooltip” but you can really change the variable to anything that makes the most sense to you and your naming conventions. We set the outline to none so no border is drawn around our element, keeping it invisible when not active. We set the position to relative so we can control the placement of it later.

What about all that :before and :after stuff? These are the pseudo elements of our parent class. We use the parent class on the element we want to use the tooltips, while the pseudo-elements are what actually make our tooltips.

And what about that weird \9 in the display property? That’s a CSS hack that targets IE9- and tells the browser to only use it for those browsers. Yes, it’s a little sloppy to have hacks, so feel free to take it out if it makes you sick.

Note that your CSS3 properties should be properly vendor-prefixed. I’m not prefixing them here for the sake of simplicity.

Activate our tooltips on data-trigger

Data attributes are a handy way of adding data to the HTML that can be targeted in CSS and Javascript while remaining invisible. We are using them in this instance to tell the browser whether to activate the tooltip on a hover state or a focus state. Hover is ideal for desktop sites, allowing the tooltip to become visible when the mouse cursor hovers over it. Focus is idea for touch devices, allowing the tooltip to display after the user has tapped the element that contains it.

This is nested inside our parent class:

// Tooltip trigger
  &[#{$trigger}="hover"]:hover:before,
  &[#{$trigger}="hover"]:hover:after,
  &[#{$trigger}="focus"]:focus:before,
  &[#{$trigger}="focus"]:focus:after {
    display: block\9; // :after:hover doesn't work with ie8
    opacity: 1;
    transition: opacity .3s linear 0s;
    visibility: visible;
    z-index: 2;
}

Now our tooltip will appear on a hover or touch when we specify it on the data-trigger attribute on our element. Note that our element will require a tabindex="0" for the focus to work. See the demo at the end for an example.

We set the opacity to 1 from 0 so the tooltip will display in full. We also give it a z-index so it will lay nicely on top of the anything it happens to run into. We also set a transition so the tooltip fades in for a subtle effect.

Add the data-placement

Similar to data-trigger we are creating another data-attribute to target whether want the tooltip to display above, below or to the left or right of an element when it is active. We are calling it data-placement and it can be nested in the parent class.

These will be the styles that define the placement of our tooltip depending on whether we choose top, bottom, left or right.

// Tooltip placement top
&[#{$placement}="top"]:after,
&[#{$placement}="top"]:before {
    bottom: 100%;
}
	
&[#{$placement}="top"]:after {
    border-radius: 4px;
    left: 50%;
    margin: 0 0 15px 0;
    transform: translate(50%);
}
	
&[#{$placement}="top"]:before {
    content: "";
    border-right: 8px solid transparent;
    border-left: 8px solid transparent;
    border-top: 8px solid black;
    left: 50%;
    margin: 0 0 7px 0;
    transform: translate(50%);
}
	
// Tooltip placement right
&[#{$placement}="right"]:after,
&[#{$placement}="right"]:before {
    left: 100%;
}
	
&[#{$placement}="right"]:after {
    border-radius: 4px;
    margin: 0 0 0 15px;
    top: 50%;
    transform: translate(50%);
}
	
&[#{$placement}="right"]:before {
    content: "";
    border-bottom: 8px solid transparent;
    border-top: 8px solid transparent;
    border-right: 8px solid black;
    margin: 0 0 0 7px;
    top: 50%;
    transform: translate(50%);
}		
	
// Tooltip placement bottom
&[#{$placement}="bottom"]:after,
&[#{$placement}="bottom"]:before {
    top: 100%;
}
	
&[#{$placement}="bottom"]:after {
    border-radius: 4px;
    left: 50%;
    margin: 15px 0 0 0;
    transform: translate(50%);
}
	
&[#{$placement}="bottom"]:before {
    content: "";
    border-right: 8px solid transparent;
    border-left: 8px solid transparent;
    border-bottom: 8px solid $black;
    left: 50%;
    margin: 8px 0 0 0;
    transform: translate(50%);
}
	
// Tooltip placement left
&[#{$placement}="left"]:after,
&[#{$placement}="left"]:before {
    right: 100%;
}
	
&[#{$placement}="left"]:after {
    border-radius: 4px;
    margin: 0 15px 0 0;
    top: 50%;
    transform: translate(50%);
}
	
&[#{$placement}="left"]:before {
    content: "";
    border-bottom: 8px solid transparent;
    border-top: 8px solid transparent;
    border-left: 8px solid $black;
    margin: 0 7px 0 0;
    top: 50%;
    transform: translate(50%);
}

Putting it All Together

Once we’ve put all of these snippets together, we have a mixin that can be used to create a tooltip anywhere.

And here it is all together:

See the Pen SASS Tooltip Mixin by Geoff Graham (@geoffgraham) on CodePen.

✏️ Handwritten by Geoff Graham on March 12, 2014