CSS Dropdown Menus and Tooltips

Build dropdown menus and tooltips with only CSS, using position absolute, :hover and :focus-within, plus accessible options with details and popover.

Lesson 26 of 49Intermediate15 min read

Dropdown menus and tooltips both show extra content on top of the page when the user asks for it. A dropdown reveals a list of links under a menu item, and a tooltip shows a short hint next to an icon or word. Both are built from the same three CSS ideas you already know: a positioned parent, an absolutely positioned child that's hidden by default, and a pseudo-class such as :hover that reveals it.

Getting the basic effect working takes a few lines. Making it work well takes a little more thought. Hover doesn't exist on touch screens, keyboard users need to reach the links with Tab, and a menu that closes the moment the mouse slips off it is frustrating. In this lesson you'll build both components with pure CSS, then fix those problems with :focus-within, careful spacing and smooth transitions.

You'll also meet two modern HTML options that need little or no JavaScript: <details> for click-to-open menus, and the Popover API, which handles showing, hiding and layering for you.

The dropdown pattern

CSS
.dropdown { position: relative; }

.dropdown-menu {
  position: absolute;
  top: 100%;
  left: 0;
  display: none;
}

.dropdown:hover .dropdown-menu,
.dropdown:focus-within .dropdown-menu { display: block; }
  • position: relative on the parent makes it the reference point for the menu, as you saw in CSS position.
  • top: 100% places the menu directly under the parent.
  • :focus-within matches the parent when anything inside it has focus, so keyboard users can open it with Tab.

The tooltip pattern

CSS
.tip { position: relative; }
.tip::after {
  content: attr(data-tip);
  position: absolute;
  bottom: calc(100% + 8px);
  left: 50%;
  transform: translateX(-50%);
  opacity: 0;
  pointer-events: none;
}
.tip:hover::after, .tip:focus-visible::after { opacity: 1; }

attr(data-tip) reads the tooltip text from a data-tip attribute, so the same CSS works for every tooltip. pointer-events: none stops the invisible tooltip catching mouse clicks. Tooltips should only hold extra hints; never hide information people need to complete a task inside one.

Smooth showing and hiding

display can't be animated in older browsers, so for fades use opacity and visibility together:

CSS
.menu { opacity: 0; visibility: hidden; transform: translateY(-4px); transition: 0.15s; }
.dropdown:hover .menu { opacity: 1; visibility: visible; transform: none; }

visibility: hidden stops the hidden menu catching clicks or keyboard focus, which opacity: 0 alone wouldn't do. Use z-index on the menu so it appears above the content that follows.

details and the Popover API

OptionHow it opensGood for
CSS :hover + :focus-withinHover or keyboard focusSimple desktop navigation
<details> + <summary>Click or tap, no JavaScriptMobile menus, filters, FAQs
popover attributeClick on a button with popovertargetMenus, tooltips, pickers

A popover sits in the browser's top layer, above everything else, closes when you click outside or press Esc, and needs no z-index battles. Style it with the [popover] selector and its backdrop with ::backdrop.

CSS Dropdowns and Tooltips Syntax

Dropdown and tooltip

.dropdown { position: relative; }
.dropdown .menu { position: absolute; top: 100%; left: 0; z-index: 10; visibility: hidden; opacity: 0; }
.dropdown:hover .menu, .dropdown:focus-within .menu { visibility: visible; opacity: 1; }
[data-tip] { position: relative; }
[data-tip]::after { content: attr(data-tip); position: absolute; bottom: 100%; }
[popover] { border: 0; border-radius: 8px; }

CSS Dropdowns and Tooltips Summary

  • Dropdowns and tooltips use a relative parent and an absolute child.
  • Reveal with :hover, and add :focus-within or :focus-visible for keyboard users.
  • Use opacity and visibility together for smooth, click-safe fades.
  • attr(data-tip) puts tooltip text in the HTML, not the CSS.
  • For touch screens, prefer click-based <details> or the popover attribute.

CSS Dropdowns and Tooltips Examples

Each example runs in the page. Edit the code and press Run to see what changes.

Example 1

A hover and keyboard dropdown

Hover over "Products", or press Tab to move into it.

.bar { display: flex; gap: 4px; background: #111827; padding: 6px; font-family: sans-serif; }
.bar a { display: block; padding: 8px 12px; color: white; text-decoration: none; }

.dropdown { position: relative; }

.menu {
  position: absolute; top: 100%; left: 0; z-index: 10;
  min-width: 170px; margin: 0; padding: 6px 0; list-style: none;
  background: white; border-radius: 8px; box-shadow: 0 8px 24px rgb(0 0 0 / 0.18);
  opacity: 0; visibility: hidden; transform: translateY(-4px);
  transition: opacity 0.15s, transform 0.15s, visibility 0.15s;
}
.menu a { color: #111827; }
.menu a:hover { background: #f3f4f6; }

.dropdown:hover .menu,
.dropdown:focus-within .menu { opacity: 1; visibility: visible; transform: none; }
ResultEdit the code, then press Run or Ctrl+Enter
Line-by-line explanation
  1. Line 4The dropdown wrapper is the positioning context.
  2. Line 7The menu sits directly under "Products" and above the page.
  3. Lines 10 to 11It starts hidden, slightly raised, and fades in.
  4. Lines 16 to 17Hover or keyboard focus inside the wrapper opens it.
Example 2

A tooltip with an arrow

.tip { position: relative; border: 0; background: #e0e7ff; padding: 4px 10px; border-radius: 999px; cursor: help; font: inherit; }

.tip::after, .tip::before {
  position: absolute; left: 50%; transform: translateX(-50%);
  opacity: 0; pointer-events: none; transition: opacity 0.15s;
}
.tip::after {
  content: attr(data-tip);
  bottom: calc(100% + 10px);
  width: max-content; max-width: 220px;
  padding: 6px 10px; border-radius: 6px;
  background: #1f2937; color: white; font-size: 13px;
}
.tip::before {
  content: "";
  bottom: calc(100% + 4px);
  border: 6px solid transparent; border-top-color: #1f2937; border-bottom: 0;
}
.tip:hover::after, .tip:hover::before,
.tip:focus-visible::after, .tip:focus-visible::before { opacity: 1; }

p { margin-top: 70px; font-family: sans-serif; }
ResultEdit the code, then press Run or Ctrl+Enter
Line-by-line explanation
  1. Line 4Both pseudo-elements are centred above the button.
  2. Line 8The text comes from the data-tip attribute.
  3. Lines 15 to 17A small triangle made from borders points down at the button.
  4. Lines 19 to 20Hover or keyboard focus shows the tooltip.
Example 3

A click menu with popover

Click the button, then click outside or press Esc.

button { font: 600 15px sans-serif; padding: 8px 14px; border-radius: 8px; border: 1px solid #d1d5db; background: white; cursor: pointer; }

[popover] {
  border: 0;
  padding: 6px;
  border-radius: 10px;
  box-shadow: 0 10px 30px rgb(0 0 0 / 0.2);
  font-family: sans-serif;
}
[popover] a { display: block; padding: 8px 14px; color: #111827; text-decoration: none; border-radius: 6px; }
[popover] a:hover { background: #f3f4f6; }
ResultEdit the code, then press Run or Ctrl+Enter
Line-by-line explanation
  1. Lines 3 to 9The browser handles opening, closing and stacking, so CSS only styles the box.
  2. Line 10Each link is a comfortable block to click.

Common CSS Dropdowns and Tooltips Mistakes

A gap that closes the menu

Don't
.menu { position: absolute; top: 100%; margin-top: 12px; }
Do
.menu { position: absolute; top: 100%; padding-top: 12px; }

The mouse crosses the margin gap, leaves the dropdown and the menu closes. Padding is part of the menu.

Hover only

Don't
.dropdown:hover .menu { display: block; }
Do
.dropdown:hover .menu,
.dropdown:focus-within .menu { display: block; }

Keyboard users can't hover. :focus-within opens the menu when a link inside it is focused.

Invisible tooltips that block clicks

Don't
.tip::after { opacity: 0; }
Do
.tip::after { opacity: 0; pointer-events: none; }

A transparent tooltip still sits over content and can swallow clicks.

CSS Dropdowns and Tooltips in Real Projects

Where you'll see them

E-commerce sites use large dropdown "mega menus" for product categories. GitHub and Google show tooltips on icon buttons. Account menus in the top corner of web apps are usually popovers. Accessible versions add aria-expanded to the trigger button and close the menu with Esc; the Popover API handles much of that automatically in modern browsers.

CSS Dropdowns and Tooltips Exercises

Answer each one and check it instantly. If you get stuck, open the hint before the answer.

Fill in the blankseasy

Open the menu when anything inside the dropdown has keyboard focus.

.dropdown: .menu {
  display: block;
}
Multiple choiceeasy

What makes the dropdown wrapper the reference point for the menu?

Multiple choicemedium

What does content: attr(data-tip) do?

Multiple choicemedium

Which HTML attribute makes a menu that opens on click and closes on Esc with no JavaScript?

Fix the bugmedium

The menu should sit directly under the trigger, be hidden by default, and appear on hover. Fix the CSS.

.dd { position: static; }
.list { position: absolute; top: 100%; left: 0; display: block; }
.dd:hover .list { display: none; }
ResultEdit the code, then press Run or Ctrl+Enter
Checks
  • Wrapper is positioned
  • Menu hidden by default
  • Hover reveals the menu

CSS Dropdowns and Tooltips Quiz

5 questions to check what you learned.

  1. Which position value does the dropdown menu itself use?

    • static
    • relative
    • sticky
    • absolute
  2. Why use visibility: hidden alongside opacity: 0?

    • It stops the hidden menu catching clicks and focus
    • It's faster
    • Opacity doesn't work alone
    • It's required for transitions
  3. Why are hover-only menus a problem on phones?

    • Phones can't show absolute elements
    • Touch screens have no hover, so the menu may never open reliably
    • Phones block CSS
    • Menus are too small
  4. Where do popovers appear?

    • Inside their parent's stacking context
    • At the bottom of the page
    • In the top layer, above everything else
    • In a new window
  5. How is the tooltip arrow usually drawn in CSS?

    • With an image
    • With text-shadow
    • With a zero-size element and one coloured border
    • With an SVG only

CSS Dropdowns and Tooltips Challenge

A dropdown that works with the keyboard

  1. Make .drop position: relative
  2. Make .panel position: absolute with top: 100% and left: 0
  3. Hide .panel with visibility: hidden and opacity: 0
  4. Show it (visibility: visible, opacity: 1) when .drop is hovered or has focus inside it
.panel { background: white; border: 1px solid #e5e7eb; }
ResultEdit the code, then press Run or Ctrl+Enter
Checks
  • Wrapper is relative
  • Panel is placed under the trigger
  • Hidden by default
  • Opens on keyboard focus
  • Opens on hover too