{ CF }

21 CSS Tooltips

IDE Function Hover

A typed-signature documentation popover for code editors. Hover the underlined function call to reveal a stacked card — badge, namespace path, signature, prose description, parameter rows, and a docs-shortcut footer — the kind of tooltip every IDE wishes it shipped by default.

Pure CSS MIT licensed

IDE Function Hover the 3rd of 21 designs in the 21 CSS Tooltips collection. The design is implemented in pure CSS — no JavaScript required. Copy the HTML and CSS panels below into your project. Because the demo is pure CSS, it works in any framework or templating engine you happen to use. The design honours prefers-reduced-motion and uses real semantic markup, so it ships accessibility-ready out of the box.

Live preview

Open in playground

The code

<div class="ide-stage">
  <div class="ide-editor">
    <div class="ide-tabs">
      <span class="ide-dot"></span><span class="ide-dot"></span><span class="ide-dot"></span>
      <span class="ide-tab">orchestrator.ts</span>
    </div>
    <div class="ide-body">
      <div class="ide-gutter">
        <div>1</div><div>2</div><div>3</div><div>4</div><div>5</div>
        <div>6</div><div>7</div><div>8</div>
      </div>
      <div class="ide-code">
        <div class="ide-line"><span class="ide-cmt">// pipeline assembled at boot</span></div>
        <div class="ide-line"><span class="ide-kw">import</span> { <span class="ide-fn">createScheduler</span> } <span class="ide-kw">from</span> <span class="ide-str">"./scheduler"</span>;</div>
        <div class="ide-line">&nbsp;</div>
        <div class="ide-line"><span class="ide-kw">const</span> <span class="ide-var">queue</span> = <span class="ide-symbol"><span class="ide-fn">createScheduler</span><span class="ide-tip">
          <span class="ide-tip-head">
            <span class="ide-tip-badge">FUNCTION</span>
            <span class="ide-tip-path"><span>core</span><span class="ide-sep">›</span><span>scheduler</span><span class="ide-sep">›</span><span>createScheduler</span></span>
          </span>
          <span class="ide-tip-sig"><span class="ide-kw">function</span> <span class="ide-fn">createScheduler</span>&lt;<span class="ide-var">T</span>&gt;(<br>&nbsp;&nbsp;<span class="ide-param">opts</span>: <span class="ide-var">SchedulerOptions</span>&lt;<span class="ide-var">T</span>&gt;<br>): <span class="ide-var">Queue</span>&lt;<span class="ide-var">T</span>&gt;</span>
          <span class="ide-tip-desc">Creates a back-pressured task queue with concurrency control. Items are processed in <code class="ide-tip-code">FIFO</code> order; failing jobs surface to the dead-letter sink.</span>
          <span class="ide-tip-params">
            <span class="ide-tip-param-row"><span class="ide-pname">concurrency</span><span class="ide-pdesc">Max parallel jobs. Default <code class="ide-tip-code">4</code>.</span></span>
            <span class="ide-tip-param-row"><span class="ide-pname">retries</span><span class="ide-pdesc">Per-task retry budget. Default <code class="ide-tip-code">3</code>.</span></span>
            <span class="ide-tip-param-row"><span class="ide-pname">onDrain</span><span class="ide-pdesc">Fires once when the queue empties.</span></span>
          </span>
          <span class="ide-tip-foot">
            <span>scheduler.ts · L42</span>
            <span><span class="ide-kbd-key">⌘</span><span class="ide-kbd-key">K</span> for docs</span>
          </span>
        </span></span>({</div>
        <div class="ide-line">&nbsp;&nbsp;<span class="ide-param">concurrency</span>: <span class="ide-num">8</span>,</div>
        <div class="ide-line">&nbsp;&nbsp;<span class="ide-param">retries</span>: <span class="ide-num">3</span>,</div>
        <div class="ide-line">});</div>
      </div>
    </div>
  </div>
</div>
/* No @import here. Demos use Inter + JetBrains Mono (from
   BaseLayout) and Georgia / cursive system fallbacks for the rest.
   See top-of-file Fonts comment for the why. */

.ide-stage {
  background: #0d1117;
  /* Top padding sized so the tooltip (~280px tall, pops up from the
     symbol) fully renders inside the gallery card. Without this the
     card's overflow:hidden clips the top of the tip. The bottom needs
     less room since the editor is anchored to flex-start. */
  padding: 300px 28px 48px;
  display: flex;
  align-items: flex-start;
  justify-content: center;
  font-family: 'JetBrains Mono', ui-monospace, monospace;
}
.ide-editor {
  background: #161b22;
  border: 1px solid #30363d;
  border-radius: 8px;
  width: 100%;
  max-width: 560px;
  box-shadow: 0 18px 50px -16px rgba(0, 0, 0, 0.7);
}
.ide-tabs {
  background: #0d1117;
  border-bottom: 1px solid #30363d;
  display: flex;
  align-items: center;
  padding: 0 14px;
  height: 32px;
  gap: 4px;
  border-radius: 8px 8px 0 0;
}
.ide-dot {
  width: 9px; height: 9px; border-radius: 50%;
  background: #30363d;
}
.ide-dot:nth-child(1) { background: #ff5f57; }
.ide-dot:nth-child(2) { background: #febc2e; }
.ide-dot:nth-child(3) { background: #28c840; }
.ide-tab {
  margin-left: 18px;
  padding: 5px 12px;
  background: #161b22;
  border-radius: 6px 6px 0 0;
  font-size: 11px;
  color: #c9d1d9;
  display: inline-flex;
  align-items: center;
  gap: 7px;
}
.ide-tab::before {
  content: ''; width: 4px; height: 4px; border-radius: 50%;
  background: #58a6ff;
}
.ide-body {
  padding: 18px 0;
  font-size: 13px;
  /* Use a pixel line-height so the gutter and code columns advance at
     the same rate — em-based line-height (1.85) combined with two
     different font-sizes drifted them apart by ~2px per row and the
     gutter numbers no longer matched their code lines. */
  line-height: 22px;
  display: flex;
  color: #c9d1d9;
}
.ide-gutter {
  width: 44px;
  text-align: right;
  padding-right: 12px;
  color: #484f58;
  user-select: none;
  border-right: 1px solid #21262d;
  /* Same font-size as the code column so digits ride the 22px baseline
     in step with the code. */
  font-size: 13px;
}
.ide-code { padding: 0 16px; flex: 1; min-width: 0; }
/* No white-space: pre on .ide-line. The line uses &nbsp; for visible
   indentation, which works under white-space: normal. Setting pre
   would honor the source newlines between sibling spans (especially
   inside the .ide-symbol that nests the multi-line tooltip markup),
   breaking each token onto its own visual row in the try-it iframe. */
.ide-line {}
.ide-kw    { color: #ff7b72; }
.ide-fn    { color: #d2a8ff; }
.ide-str   { color: #a5d6ff; }
.ide-num   { color: #79c0ff; }
.ide-cmt   { color: #8b949e; font-style: italic; }
.ide-var   { color: #79c0ff; }
.ide-param { color: #ffa657; }

.ide-symbol {
  position: relative;
  cursor: help;
  /* Visible-at-rest affordance: a dashed blue underline tells users
     "this token has a hover" without needing them to land on it first.
     The original (transparent until hover) was a discovery failure —
     users had no signal the demo had a hover tooltip at all. */
  border-bottom: 1px dashed rgba(88, 166, 255, 0.55);
  transition: border-color 0.2s, background 0.2s;
  border-radius: 2px;
  padding: 0 2px;
}
.ide-symbol::after {
  /* Small info dot to the right of the symbol — second discovery cue
     in case the underline gets lost in syntax highlighting. */
  content: 'ⓘ';
  display: inline-block;
  font-size: 9px;
  color: rgba(88, 166, 255, 0.6);
  vertical-align: middle;
  margin-left: 3px;
  transition: color 0.2s;
}
.ide-symbol:hover {
  border-color: #58a6ff;
  background: rgba(88, 166, 255, 0.08);
}
.ide-symbol:hover::after {
  color: #58a6ff;
}

.ide-tip {
  position: absolute;
  bottom: calc(100% + 14px);
  left: -20px;
  width: 380px;
  background: linear-gradient(180deg, #1c2128 0%, #161b22 100%);
  border: 1px solid #30363d;
  border-radius: 6px;
  box-shadow:
    0 18px 50px -8px rgba(0, 0, 0, 0.75),
    0 0 0 1px rgba(88, 166, 255, 0.06),
    inset 0 1px 0 rgba(255, 255, 255, 0.04);
  opacity: 0;
  visibility: hidden;
  transform: translateY(8px);
  transition:
    opacity 0.22s ease,
    transform 0.22s cubic-bezier(0.22, 1, 0.36, 1),
    visibility 0s linear 0.22s;
  z-index: 10;
  font-family: 'JetBrains Mono', ui-monospace, monospace;
  pointer-events: none;
  display: block;
  /* Reset two properties the tip inherits from the .ide-line ancestor:
     white-space: pre (which would render the source newlines between
     spans as visible whitespace inside the tip) and line-height: 1.85
     (the editor's loose line-height — fine for code, way too tall
     inside the tooltip's prose). */
  white-space: normal;
  line-height: 1.5;
  text-align: left;
}
.ide-symbol:hover .ide-tip {
  opacity: 1;
  visibility: visible;
  transform: translateY(0);
  transition-delay: 0s;
}
.ide-tip::after {
  content: '';
  position: absolute;
  top: 100%;
  left: 32px;
  width: 10px; height: 10px;
  background: #161b22;
  border-right: 1px solid #30363d;
  border-bottom: 1px solid #30363d;
  transform: translateY(-50%) rotate(45deg);
}
.ide-tip-head {
  padding: 10px 14px 9px;
  border-bottom: 1px solid #21262d;
  display: flex;
  align-items: center;
  gap: 10px;
  font-size: 11px;
}
.ide-tip-badge {
  background: rgba(88, 166, 255, 0.15);
  color: #58a6ff;
  padding: 2px 7px;
  border-radius: 10px;
  font-size: 9.5px;
  font-weight: 600;
  letter-spacing: 0.05em;
}
.ide-tip-path { color: #8b949e; font-size: 11px; }
.ide-sep { color: #484f58; margin: 0 4px; }
.ide-tip-sig {
  padding: 12px 14px;
  font-size: 12px;
  line-height: 1.6;
  border-bottom: 1px solid #21262d;
  display: block;
  /* Re-enable pre here so the multi-line function signature keeps its
     indent. The outer .ide-tip reset this to normal so the prose
     blocks (description, params, footer) don't render the source-
     formatting whitespace between sibling spans. */
  white-space: pre;
}
.ide-tip-desc {
  padding: 12px 14px;
  font-family: 'Inter', system-ui, sans-serif;
  font-size: 12px;
  line-height: 1.65;
  color: #c9d1d9;
  border-bottom: 1px solid #21262d;
  display: block;
}
.ide-tip-code {
  font-family: 'JetBrains Mono', ui-monospace, monospace;
  background: rgba(110, 118, 129, 0.2);
  color: #ffa657;
  padding: 1px 5px;
  border-radius: 3px;
  font-size: 11px;
}
.ide-tip-params {
  padding: 10px 14px;
  border-bottom: 1px solid #21262d;
  display: block;
}
.ide-tip-param-row {
  display: grid;
  grid-template-columns: 90px 1fr;
  gap: 10px;
  font-size: 11.5px;
  padding: 3px 0;
  line-height: 1.5;
}
.ide-pname { color: #ffa657; }
.ide-pdesc { color: #8b949e; font-family: 'Inter', system-ui, sans-serif; font-size: 11.5px; }
.ide-tip-foot {
  padding: 9px 14px;
  font-size: 10px;
  color: #6e7681;
  display: flex;
  justify-content: space-between;
  align-items: center;
}
.ide-kbd-key {
  background: #21262d;
  border: 1px solid #30363d;
  border-bottom-width: 2px;
  color: #c9d1d9;
  padding: 1px 6px;
  border-radius: 4px;
  font-size: 10px;
  margin: 0 2px;
  display: inline-block;
}

Search CodeFronts

Loading…