WordPress Component Library
10up Engineering

Tooltip

The tooltip component is a standard and accessible JavaScript plugin that was converted from a jQuery plugin by Scott Ohara, you can view the jQuery plugin on Github. No jQuery is required for this version.

It can be used in three forms:

  1. a standard hover tooltip over a link/button/text input;
  2. a hover tooltip over a non-focusable element (like a <span>, the JavaScript will make it accessible);
  3. a togglable tooltip (click to hide/show).

There are also delay and timing options available, however the default timing of 1 second mimics the title attribute timing (which was the previous WCAG standard; timing requirements have since been removed).

The converted plugin we’re using is also hosted on Github, you can download the plugin itself.

HTML Download the plugin

<!--
	A Tooltip with a span
-->

<span class="a11y-tip">
	<span class="a11y-tip__trigger">
		Tooltip Trigger span
	</span>

	<span class="a11y-tip__help">
		Tooltip content goes here
	</span>
</span><!--/.a11y-tip-->

<!--
	A Tooltip with a link
-->

<span class="a11y-tip a11y-tip--no-delay">
	<a href="#!" class="a11y-tip__trigger">
		Link w/top tooltip
	</a>

	<span class="a11y-tip__help a11y-tip__help--top">
		Activate this link to go somewhere!
	</span>
</span><!--/.a11y-tip-->

<!--
	A Tooltip with a button
-->

<span class="a11y-tip">
	<button type="button" class="a11y-tip__trigger" aria-describedby="tt_id">
		Button w/bottom tooltip
	</button>

	<span id="tt_id" role="tooltip" class="a11y-tip__help a11y-tip__help--bottom">
		Buttons do things on the page. Activate it to perform an action.
	</span>
</span><!--/.a11y-tip-->

<!--
	A Tooltip with an input
-->

<label for="test">
	Input with right tooltip
</label>
<span class="a11y-tip">
	<input id="test" placeholder="enter text" class="a11y-tip__trigger" aria-describedby="test_desc" type="text">

	<span id="test_desc" role="tooltip" class="a11y-tip__help a11y-tip__help--right">
		Enter something here. Text would be fine.
	</span>
</span><!--/.a11y-tip-->

<!--
	Click to show/hide tooltip
-->

<span class="a11y-tip a11y-tip--toggle">
	<span class="a11y-tip__trigger">
		Tooltip Toggle Trigger
	</span>

	<span class="a11y-tip__help">
		Tooltip content goes here
	</span>
</span><!--/.a11y-tip-->

SCSS Download SCSS

// Breakpoints
$bp-small:          40.625em !default;      // 650px
$bp-medium:         53.125em !default;      // 850px
$bp-large:          62.5em !default;        // 1000px

// Mixins
@mixin triangle($tri-size, $tri-color, $tri-direction) {
	border: inset $tri-size;
	content: '';
	display: inline-block;
	height: 0;
	speak: none;
	width: 0;

	@if ($tri-direction == top) {
		border-color: $tri-color transparent transparent transparent;
		border-top-style: solid;
	}

	@if ($tri-direction == bottom) {
		border-color: transparent transparent $tri-color transparent;
		border-bottom-style: solid;
	}

	@if ($tri-direction == left) {
		border-color: transparent transparent transparent $tri-color;
		border-left-style: solid;
	}

	@if ($tri-direction == right) {
		border-color: transparent $tri-color transparent transparent;
		border-right-style: solid;
	}
}

// Namespace, if you want it (update the HTML too though)
$ns: '' !default;

// Varables
$tip--color-white: #fff !default;
$tip--color-black: #000 !default;
$tip--color-gray: #666 !default;

$tip--bg: fade-out($tip--color-black, .1) !default;
$tip--base-size: 12px !default;
$tip--arrow-size: 6px !default;

$tip--trigger--border-bottom: 1px dotted $tip--color-gray !default;

// Setting flags for the types you need because right & left need
// extra CSS to be cancelled out on small screens - let's not generate
// CSS we don't need, OK?... OK?!

$tip--compile-bottom: true !default;
$tip--compile-left: true !default;
$tip--compile-right: true !default;

$tip--delay: 1s !default;
$tip--no-delay: .18s !default;

// ---------------------------------------------------------------------

// parent wrapper
.#{$ns}a11y-tip {
	display: inline-block;
	position: relative;

	// element that triggers the tooltip (help) pop-up
	&__trigger {

		// if not an element that has inherit focus, add visual
		// affordance to let users know this element is not just
		// regular text.
		&[tabindex] {
			border-bottom: $tip__trigger--border-bottom;
		}

		// on hover/focus of the trigger, reveal the help which
		// is placed directly after the trigger in the DOM
		// (as you would not describe something before you know
		// what that something is.)
		&[aria-describedby]:hover + ,
		&[aria-describedby]:not(.a11y-tip__trigger--toggle):focus +,
		&[aria-describedby][aria-expanded="true"] {

			.a11y-tip__help,
			.a11y-tip__help:after {
				opacity: 1;
				pointer-events: auto;
				visibility: visible;
			}

			.a11y-tip__help {

				&,
				&:after {
					transform: translate(-50%, 0px);
				}

				@media screen and ( min-width: $bp-small ) {

					@if $tip--compile-left == true {
						&--left,
						&--left:after{
							transform: translate(0px, -50%);
						}
					} // end if

					@if $tip--compile-right == true {
						&--right,
						&--right:after {
							transform: translate(0px, -50%);
						}
					} // end if

				} // end @media

			} // .a11y-tip__help

		} // end :hover, :focus

		&[aria-describedby].a11y-tip__trigger--toggle:hover + {
			.a11y-tip__help,
			.a11y-tip__help:after {
				opacity: 0;
				pointer-events: none;
				visibility: hidden;
			}
		}

		&[aria-expanded="true"].a11y-tip__trigger--toggle:hover + {
			.a11y-tip__help,
			.a11y-tip__help:after {
				opacity: 1 !important;
				pointer-events: auto !important;
				transition: opacity $tip--no-delay ease-in-out,
									transform $tip--no-delay ease-in-out;
				visibility: visible !important;
			}
		}

	}

	// by default tooltips have a 1s delay to mimic native tooltips
	// but if you want/need tooltips to show up faster, use this class
	&--no-delay {

		.a11y-tip__help,
		.a11y-tip__help:after {
			transition: opacity $tip--no-delay ease-in-out,
									transform $tip--no-delay ease-in-out;
		}
	}
}


[role="tooltip"].a11y-tip__help {
	background: $tip--bg;
	border-radius: 4px;
	color: $tip--color-white;
	font-size: $tip--base-size + 2;
	left: 50%;
	margin-top: 6px + $tip--arrow-size;
	max-width: 400px;
	min-width: 220px;
	opacity: 0;
	padding: .5em 1em;
	pointer-events: none;
	position: absolute;
	text-align: left;
	top: 100%;
	transform: translate(-50%, 10px);
	transform-style: preserve-3d;
	transition: opacity .2s $tip--delay ease-in-out;
	visibility: hidden;
	width: 100%;
	z-index: 10;

	&:after {
		@include triangle( $tip--arrow-size, $tip--bg, bottom );
		bottom: 100%;
		content: '';
		left: 50%;
		margin-top: 6px;
		opacity: 0;
		pointer-events: none;
		position: absolute;
		transform: translate(-50%, 10px);
		transform-style: preserve-3d;
		transition: opacity .2s $tip--delay ease-in-out;
		visibility: hidden;
		z-index: 10;
	}


	@if $tip--compile-bottom == true {
		// reposition tip to the top
		&--top {
			bottom: 100%;
			left: 50%;
			margin-bottom: 6px + $tip--arrow-size;
			margin-top: 0; // cancel bottom default styling
			top: auto; // cancel bottom default styling
			transform-origin: center top;
			transform: translate(-50%, 10px);

			&:after {
				@include triangle( $tip--arrow-size, $tip--bg, top );
				left: 50%;
				margin-bottom: 6px;
				margin-top: 0; // cancel bottom default styling
				top: 100%;
				transform-origin: center top;
				transform: translate(-50%, -10px);
			}
		}
	}


	// Left and Right positions are only useful on larger sized
	// screens.  So they should go back to the bottom default
	// positioning/styling when a screen isn't wide enough.
	@media screen and ( min-width: $bp-small ) {

		@if $tip--compile-left == true {
			&--left {
			left: auto; // cancel bottom default styling
				margin-right: 5px + $tip--arrow-size;
				margin-top: 0;
				right: 100%;
				top: 50%;
				transform: translate(10px, -50%);

				&:after {
					@include triangle( $tip--arrow-size, $tip--bg, left );
					left: 100%;
					margin-right: 5px;
					margin-top: 0; // cancel bottom default styling
					top: 50%;
					transform: translate(-10px, -50%);
				}
			}
		} // end @if left


		@if $tip--compile-right == true {
			&--right {
				left: 100%;
				margin-left: 5px + $tip--arrow-size;
				margin-top: 0; // cancel bottom default styling
				top: 50%;
				transform: translate(10px, -50%);

				&:after {
					@include triangle( $tip--arrow-size, $tip--bg, right );
					bottom: auto; // cancel bottom default styling
					left: auto; // cancel bottom default styling
					margin-left: 5px;
					margin-top: 0;  // cancel bottom default styling
					right: 100%;
					top: 50%;
					transform: translate(10px, -50%);
				}
			}
		} // end @if right
	}
}


// hide tool tips if the ESC key is hit
.#{$ns}a11y-tip--hide .#{$ns}a11y-tip__help {
	display: none;
}


.no-js .#{$ns}a11y-tip__help:not([role]) {
	font-size: .85em;

	&:before {
		content: "(";
	}

	&:after {
		content: ")";
	}
}

JS Usage | Plugin

TenUp.tooltips( function() {
	console.log( 'Amazing callback function!' );
} );

JavaScript API

TenUp.tooltips( callback );

This is the main method to call the plugin. It currently takes one argument: a callback function. It targets the .a11y_tip class automatically.

callback

Accepts a function that will be executed upon completion.

Browser Compatibility

Feature Chrome Firefox Internet Explorer Safari
Basic Support Latest Latest 9+ 6+ [1]

[1] Tooltips triggered by ‘:hover’ work in Safari 5

Resources