Transform Your Web Development With Accessible Tooltips
A lightweight, accessible tooltip solution that provides informational and help tooltips with various positioning options. The tooltips are keyboard-accessible and customizable through CSS/SCSS.
Key Features:
- Two distinct tooltip styles: Info (ℹ️) and Help (?)
- Multiple positioning options that dynamically adjust based on viewport position:
- Top left
- Top center
- Top right
- Bottom left
- Bottom center
- Bottom right
- Full keyboard accessibility
- Automatic repositioning on window resize
- Tooltips close on escape key, scroll, or click outside
- Support for HTML content within tooltips
- Responsive design
- To enhance accessibility, ARIA attributes are added for screen reader support
- For simplified configuration, the tooltip message is generated from the data-content attribute on the cl-tooltip link
- Finally, it’s built with Vanilla Javascript, therefore requiring no dependencies
To see it in action, you can view the Accessible tooltips example either on Codepen or GITHUB.
Live example:
Nullam porta odio maximus lobortis imperdiet. Vivamus non accumsan nibh. Nulla ultrices gravida neque, non accumsan nibh ultrices vitae. In ultricies neque sed magna tempus, sit amet condimentum libero viverra. Phasellus ornare fringilla mollis. Proin et elit at dolor volutpat maximus faucibus quis nunc. Nullam ornare justo ac est ullamcorper, a scelerisque justo feugiat. Proin sed ante et massa tristique gravida non quis odio. Vivamus ac ligula ipsum. Vestibulum ac commodo risus. Maecenas sit amet tempus quam, eget auctor mauris. Vestibulum ac dolor ligula.
Messar tincidunt leo libero, at ullamcorper sem sagittis vel. Integer nisl massa, condimentum at libero eget, gravida viverra lacus. Curabitur vel ornare orci, quis blandit leo. Nulla tortor est, tempor ac consequat a, porta quis elit. Proin consequat mi in elit aliquam tristique. Integer isl massa. Aenean est sem, placerat non magna eu, ornare faucibus tortor. Proin in nisl blandit sem blandit rhoncus nec nec tellus. Donec quis nisl neque. Nam mi eros, consectetur eu ex sed, elementum commodo tortor. Curabitur et varius arcu, quis sodales massa. Pellentesque imperdiet neque massa, eget suscipit augue condimentum sit amet. Pellentesque felis est, fermentum eget tempor ac, dignissim vel eros. Phasellus ac erat non risus lacinia rutrum. Quisque sagittis lacus nec lectus lacinia facilisis. Duis ac dui ipsum. Proin accumsan condimentum volutpat.
Nullam porta odio maximus lobortis imperdiet. Vivamus non accumsan nibh. Nulla ultrices gravida neque, non accumsan nibh ultrices vitae. In ultricies neque sed magna tempus, sit amet condimentum libero viverra. Phasellus ornare fringilla mollis. Proin et elit at dolor volutpat maximus faucibus quis nunc. Nullam ornare justo ac est ullamcorper, a scelerisque justo feugiat. Proin sed ante et massa tristique gravida non quis odio. Vivamus ac ligula ipsum. Vestibulum ac commodo risus. Maecenas sit amet tempus quam, eget auctor mauris. Vestibulum ac dolor ligula.
Code samples
HTML:
<p>Nullam porta odio maximus lobortis imperdiet. Vivamus non accumsan nibh. Nulla ultrices gravida neque, non accumsan nibh ultrices vitae. In ultricies neque sed magna tempus, sit amet condimentum libero viverra.<a href="#" class="tooltip tooltip-help" data-content="help tooltip description" title="help tooltip" data-mce-href="#"> </a> Phasellus ornare fringilla mollis. Proin et elit at dolor volutpat maximus faucibus quis nunc. Nullam ornare justo ac est ullamcorper, a scelerisque justo feugiat. Proin sed ante et massa tristique gravida non quis odio. Vivamus ac ligula ipsum.<a href="#" class="tooltip tooltip-info" data-content="information tooltip" title="info tooltip" data-mce-href="#"> </a> Vestibulum ac commodo risus. Maecenas sit amet tempus quam, eget auctor mauris. Vestibulum ac dolor ligula.</p><p><a class="tooltip" href="#" data-content="tooltip description 1. A short sentence. <a href='#'>a link</a> and <a href='#'>a link2</a>" data-mce-href="#">Messar</a> tincidunt leo libero, at ullamcorper sem sagittis vel. Integer nisl <a class="tooltip" href="#" data-content="tooltip description 2. A short sentence." data-mce-href="#">massa</a>, condimentum at libero eget, gravida viverra lacus. Curabitur vel <a class="tooltip" href="#" data-content="tooltip description 3. A short sentence." data-mce-href="#">ornare</a> orci, quis blandit leo. Nulla tortor est, tempor ac consequat a, porta quis elit. Proin consequat mi in elit aliquam <a class="tooltip" href="#" data-content="tooltip description 4. A short sentence." data-mce-href="#">tristique</a>. Integer isl <a class="tooltip" href="#" data-content="tooltip descriptio 5. A short sentence." data-mce-href="#">massa</a>. Aenean est sem, placerat non magna eu, ornare faucibus tortor. Proin in nisl blandit sem blandit rhoncus nec nec tellus. Donec quis nisl neque. Nam mi eros, consectetur eu ex sed, elementum commodo tortor. Curabitur et varius arcu, quis sodales massa. Pellentesque imperdiet neque massa, eget suscipit augue condimentum sit amet. Pellentesque felis est, fermentum <a class="tooltip" href="#" data-content="tooltip descriptio 6. A short sentence." data-mce-href="#">eget</a> tempor ac, dignissim vel eros. <a class="tooltip" href="#" data-content="tooltip descriptio 7. A short sentence." data-mce-href="#">Phasellus</a> ac erat non risus lacinia rutrum. Quisque sagittis lacus nec lectus lacinia facilisis. Duis ac dui <a class="tooltip" href="#" data-content="tooltip descriptio 8. A short sentence." data-mce-href="#">ipsum</a>. Proin accumsan condimentum volutpat. </p><p>Nullam porta odio maximus lobortis imperdiet. Vivamus non accumsan nibh. Nulla ultrices gravida neque, non accumsan nibh ultrices vitae. In ultricies neque sed magna tempus, sit amet condimentum libero viverra. Phasellus ornare fringilla mollis. Proin et elit at dolor volutpat maximus faucibus quis nunc. Nullam ornare justo ac est ullamcorper, a scelerisque justo feugiat. Proin sed ante et massa tristique gravida non quis odio. Vivamus ac ligula ipsum. Vestibulum ac commodo risus. Maecenas sit amet tempus quam, eget auctor mauris. Vestibulum ac dolor ligula.</p>
CSS:
html { height: 100%; } .d-none { display: none; } .tooltip-content { background-color: #F2F9FF; padding: 16px; border: 1px solid #757575; border-radius: 0.25rem; max-width: 200px; position:absolute; z-index: 1; margin-top: .5rem; } .tooltip-content::before { content: " "; width: 0.5625rem; height: 0.4375rem; background-image: url('https://www.canadapost-postescanada.ca/cpc/assets/cpc/uploads/campaigns/campaign-library/animations/tooltip-arrow.svg'); background-repeat: no-repeat; transform: rotate(180deg); position: absolute; bottom: 100%; left: 0.625rem; }
.tooltip-content.tt-bottom::before { bottom: unset; transform: rotate(0deg); top: 100%; } .tooltip-content.tt-top-mid::before { left: calc(50% - 0.28125rem); } .tooltip-content.tt-top-right::before { left: unset; right: 0.625rem; } .tooltip-info, .tooltip-help { width: 1rem; height: 1rem; display: inline-block; background-repeat: no-repeat; border-radius: 50%; text-decoration: none; }
.tooltip-info:focus-visible, .tooltip-help:focus-visible { outline: 2px solid #000; text-decoration: none; } .tooltip-info { background-image: url('https://www.canadapost-postescanada.ca/cpc/assets/cpc/uploads/campaigns/campaign-library/animations/info-icon-default.svg'); } .tooltip-info:hover { background-image: url('https://www.canadapost-postescanada.ca/cpc/assets/cpc/uploads/campaigns/campaign-library/animations/info-icon-hover.svg'); } .tooltip-info:active, .tooltip-info.active { background-image: url('https://www.canadapost-postescanada.ca/cpc/assets/cpc/uploads/campaigns/campaign-library/animations/info-icon-selected.svg'); text-decoration: none; } .tooltip-help { background-image: url('https://www.canadapost-postescanada.ca/cpc/assets/cpc/uploads/campaigns/campaign-library/animations/help-icon-default.svg'); } .tooltip-help:hover { background-image: url('https://www.canadapost-postescanada.ca/cpc/assets/cpc/uploads/campaigns/campaign-library/animations/help-icon-hover.svg'); } .tooltip-help:active, .tooltip-help.active { background-image: url('https://www.canadapost-postescanada.ca/cpc/assets/cpc/uploads/campaigns/campaign-library/animations/help-icon-selected.svg'); text-decoration: none; } .tooltip-icon-pos { margin-left: -0.46rem; margin-bottom: 0.15rem; } .tooltip-icon-pos.tt-top-right { margin-left: unset; margin-right: -0.46rem; } .tooltip-icon-pos.tt-top-mid { margin-left: unset; }
Javscript:
document.addEventListener("DOMContentLoaded", function(event) {
let tooltips = document.querySelectorAll('.tooltip'); let tooltipcontent = document.querySelector('.tooltip-content'); // function to add aria hidden and remove active function function hiddenandremoveactive() { tooltipcontent.classList.add('d-none'); tooltipcontent.setAttribute('aria-hidden', 'true'); tooltips.forEach(function(tooltip, index) { tooltip.classList.remove('active'); }); } // Check if click outside of tooltip and close document.addEventListener('click', function(e) { const isClickInside = tooltipcontent.contains(e.target); if (!isClickInside && !e.target.classList.contains('tooltip')) { hiddenandremoveactive(); } }); document.addEventListener('keydown', (event) => { // conditions for tabbing if (event.key === "Tab") { setTimeout(() => { // check if element has tooltip class let tooltiplinkparent = document.activeElement.parentElement; if (!tooltiplinkparent.classList.contains("tooltip-content")){ hiddenandremoveactive(); } console.log(tooltiplinkparent); }, 100); } // Check escape key and close tooltip if (event.key === 'Escape') { hiddenandremoveactive(); } }); //make an on scroll close tooltip document.addEventListener("scroll",function(e) { setTimeout(() => { hiddenandremoveactive(); }, 100); }); // Add click event listener and set positions tooltips.forEach(function(tooltip, index) { // create and add id to tooltip triggers let logtooltipid = "tooltip-"+(index+1); tooltip.setAttribute("id", logtooltipid); // on spacebar action trigger click tooltip.addEventListener('keydown', function(e) { if(e.keyCode == 32){ tooltip.click(); e.preventDefault(); } }); tooltip.addEventListener('click', function(e) { hiddenandremoveactive(); //add active class tooltip.classList.add('active'); tooltipcontent.setAttribute("aria-describedby", logtooltipid); //attach tooltip content tooltip.after(tooltipcontent); //function to set positioning of tooltip function repositiontooltip() {
let tooltiptop = tooltip.getBoundingClientRect().top; let tooltipleft = tooltip.getBoundingClientRect().left; let tooltipright = tooltip.getBoundingClientRect().right; let tooltipheight = tooltip.getBoundingClientRect().height; let tooltipwidth = tooltip.getBoundingClientRect().width; let tooltipcontentwidth = tooltip.nextElementSibling.getBoundingClientRect().width; let tooltiphtmlwidth = document.querySelector('html').getBoundingClientRect().width; let tooltiphtmlheight = document.querySelector('html').getBoundingClientRect().height; let tooltiphtmltop = document.querySelector('html').getBoundingClientRect().top; let tooltiptruetop = Math.abs(tooltiphtmltop) + tooltiptop;
//reset bottom class tooltipcontent.classList.remove('tt-bottom'); //reset position class for helper and info icons tooltipcontent.classList.remove('tooltip-icon-pos'); // add position class for helper and info content if exists on trigger if (tooltip.classList.contains("tooltip-info") || tooltip.classList.contains("tooltip-help")){ tooltipcontent.classList.add('tooltip-icon-pos'); } // check height and set top or bottom position if (tooltiptop < (tooltiphtmlheight * 0.5)) { tooltipcontent.style.top = tooltiptruetop + tooltipheight + "px"; tooltipcontent.style.bottom = "unset"; } else { tooltipcontent.style.top = "unset"; tooltipcontent.style.bottom = tooltiphtmlheight - tooltiptruetop + 4 + "px"; tooltipcontent.classList.add('tt-bottom'); } //reset top classes tooltipcontent.classList.remove('tt-top-mid'); tooltipcontent.classList.remove('tt-top-right'); // if top left if (tooltipleft < (tooltiphtmlwidth * 0.333)) { tooltipcontent.style.left = tooltipleft + "px"; tooltipcontent.style.right = "unset"; } // if top middle if (tooltipleft > (tooltiphtmlwidth * 0.333) && tooltipleft < (tooltiphtmlwidth * 0.666) ) { tooltipcontent.style.left = (tooltipleft + (tooltipwidth/2)) - (tooltipcontentwidth/2) + "px"; tooltipcontent.style.right = "unset"; tooltipcontent.classList.add('tt-top-mid'); } // if top right if (tooltipleft > (tooltiphtmlwidth * 0.666)) { tooltipcontent.style.left = "unset"; tooltipcontent.style.right = tooltiphtmlwidth - tooltipright + "px"; tooltipcontent.classList.add('tt-top-right'); } } tooltipcontent.innerHTML = tooltip.getAttribute('data-content') ;
tooltipcontent.classList.remove('d-none'); tooltipcontent.setAttribute('aria-hidden', 'false');
repositiontooltip(); // end repositiontooltip function //resize script conditions to reposition toolip window.addEventListener("resize", function(e){ repositiontooltip(); }); e.preventDefault(); }); });
});