21 CSS Tooltips03 / 21

Pure CSSMIT licensed

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.

Published

Live Demo
Try it

The code

<div class="tp-03">
  <div class="tp-03__editor">
    <div class="tp-03__tabs">
      <span class="tp-03__dot"></span><span class="tp-03__dot"></span><span class="tp-03__dot"></span>
      <span class="tp-03__tab">orchestrator.ts</span>
    </div>
    <div class="tp-03__body">
      <div class="tp-03__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="tp-03__code">
        <div class="tp-03__line"><span class="tp-03__cmt">// pipeline assembled at boot</span></div>
        <div class="tp-03__line"><span class="tp-03__kw">import</span> { <span class="tp-03__fn">createScheduler</span> } <span class="tp-03__kw">from</span> <span class="tp-03__str">"./scheduler"</span>;</div>
        <div class="tp-03__line">&nbsp;</div>
        <div class="tp-03__line"><span class="tp-03__kw">const</span> <span class="tp-03__var">queue</span> = <span class="tp-03__symbol"><span class="tp-03__fn">createScheduler</span><span class="tp-03__tip">
          <span class="tp-03__tip-head">
            <span class="tp-03__tip-badge">FUNCTION</span>
            <span class="tp-03__tip-path"><span>core</span><span class="tp-03__sep">›</span><span>scheduler</span><span class="tp-03__sep">›</span><span>createScheduler</span></span>
          </span>
          <span class="tp-03__tip-sig"><span class="tp-03__kw">function</span> <span class="tp-03__fn">createScheduler</span>&lt;<span class="tp-03__var">T</span>&gt;(<br>&nbsp;&nbsp;<span class="tp-03__param">opts</span>: <span class="tp-03__var">SchedulerOptions</span>&lt;<span class="tp-03__var">T</span>&gt;<br>): <span class="tp-03__var">Queue</span>&lt;<span class="tp-03__var">T</span>&gt;</span>
          <span class="tp-03__tip-desc">Creates a back-pressured task queue with concurrency control. Items are processed in <code class="tp-03__tip-code">FIFO</code> order; failing jobs surface to the dead-letter sink.</span>
          <span class="tp-03__tip-params">
            <span class="tp-03__tip-param-row"><span class="tp-03__pname">concurrency</span><span class="tp-03__pdesc">Max parallel jobs. Default <code class="tp-03__tip-code">4</code>.</span></span>
            <span class="tp-03__tip-param-row"><span class="tp-03__pname">retries</span><span class="tp-03__pdesc">Per-task retry budget. Default <code class="tp-03__tip-code">3</code>.</span></span>
            <span class="tp-03__tip-param-row"><span class="tp-03__pname">onDrain</span><span class="tp-03__pdesc">Fires once when the queue empties.</span></span>
          </span>
          <span class="tp-03__tip-foot">
            <span>scheduler.ts · L42</span>
            <span><span class="tp-03__kbd-key">⌘</span><span class="tp-03__kbd-key">K</span> for docs</span>
          </span>
        </span></span>({</div>
        <div class="tp-03__line">&nbsp;&nbsp;<span class="tp-03__param">concurrency</span>: <span class="tp-03__num">8</span>,</div>
        <div class="tp-03__line">&nbsp;&nbsp;<span class="tp-03__param">retries</span>: <span class="tp-03__num">3</span>,</div>
        <div class="tp-03__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. */

.tp-03 {
  width: 100%;
  min-height: 100vh;
  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;
}

.tp-03__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);
}

.tp-03__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;
}

.tp-03__dot {
  width: 9px;
  height: 9px;
  border-radius: 50%;
  background: #30363d;
}

.tp-03__dot:nth-child(1) {
  background: #ff5f57;
}

.tp-03__dot:nth-child(2) {
  background: #febc2e;
}

.tp-03__dot:nth-child(3) {
  background: #28c840;
}

.tp-03__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;
}

.tp-03__tab::before {
  content: '';
  width: 4px;
  height: 4px;
  border-radius: 50%;
  background: #58a6ff;
}

.tp-03__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;
}

.tp-03__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;
}

.tp-03__code {
  padding: 0 16px;
  flex: 1;
  min-width: 0;
}
/* No white-space: pre on .tp-03__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 .tp-03__symbol that nests the multi-line tooltip markup),
   breaking each token onto its own visual row in the try-it iframe. */

.tp-03__line {
}

.tp-03__kw {
  color: #ff7b72;
}

.tp-03__fn {
  color: #d2a8ff;
}

.tp-03__str {
  color: #a5d6ff;
}

.tp-03__num {
  color: #79c0ff;
}

.tp-03__cmt {
  color: #8b949e;
  font-style: italic;
}

.tp-03__var {
  color: #79c0ff;
}

.tp-03__param {
  color: #ffa657;
}

.tp-03__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;
}

.tp-03__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;
}

.tp-03__symbol:hover {
  border-color: #58a6ff;
  background: rgba(88, 166, 255, 0.08);
}

.tp-03__symbol:hover::after {
  color: #58a6ff;
}

.tp-03__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 .tp-03__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;
}

.tp-03__symbol:hover .tp-03__tip {
  opacity: 1;
  visibility: visible;
  transform: translateY(0);
  transition-delay: 0s;
}

.tp-03__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);
}

.tp-03__tip-head {
  padding: 10px 14px 9px;
  border-bottom: 1px solid #21262d;
  display: flex;
  align-items: center;
  gap: 10px;
  font-size: 11px;
}

.tp-03__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;
}

.tp-03__tip-path {
  color: #8b949e;
  font-size: 11px;
}

.tp-03__sep {
  color: #484f58;
  margin: 0 4px;
}

.tp-03__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 .tp-03__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;
}

.tp-03__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;
}

.tp-03__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;
}

.tp-03__tip-params {
  padding: 10px 14px;
  border-bottom: 1px solid #21262d;
  display: block;
}

.tp-03__tip-param-row {
  display: grid;
  grid-template-columns: 90px 1fr;
  gap: 10px;
  font-size: 11.5px;
  padding: 3px 0;
  line-height: 1.5;
}

.tp-03__pname {
  color: #ffa657;
}

.tp-03__pdesc {
  color: #8b949e;
  font-family: 'Inter', system-ui, sans-serif;
  font-size: 11.5px;
}

.tp-03__tip-foot {
  padding: 9px 14px;
  font-size: 10px;
  color: #6e7681;
  display: flex;
  justify-content: space-between;
  align-items: center;
}

.tp-03__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;
}
Paste this into ChatGPT, Claude, Cursor, or any coding assistant. The block below is pre-framed with everything the AI needs to integrate this demo into your project — markup, styles, scoping notes, and the source URL. Hit Copy and paste straight into your chat.
Here's a working CSS Tooltip from CodeFronts. Use it as-is or adapt to your framework. All classes are scoped under a unique prefix so the code won't collide with your existing styles. MIT licensed.
Demo: IDE Function Hover
Source: https://codefronts.com/snippets/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.
## HTML
```html
<div class="tp-03">
  <div class="tp-03__editor">
    <div class="tp-03__tabs">
      <span class="tp-03__dot"></span><span class="tp-03__dot"></span><span class="tp-03__dot"></span>
      <span class="tp-03__tab">orchestrator.ts</span>
    </div>
    <div class="tp-03__body">
      <div class="tp-03__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="tp-03__code">
        <div class="tp-03__line"><span class="tp-03__cmt">// pipeline assembled at boot</span></div>
        <div class="tp-03__line"><span class="tp-03__kw">import</span> { <span class="tp-03__fn">createScheduler</span> } <span class="tp-03__kw">from</span> <span class="tp-03__str">"./scheduler"</span>;</div>
        <div class="tp-03__line">&nbsp;</div>
        <div class="tp-03__line"><span class="tp-03__kw">const</span> <span class="tp-03__var">queue</span> = <span class="tp-03__symbol"><span class="tp-03__fn">createScheduler</span><span class="tp-03__tip">
          <span class="tp-03__tip-head">
            <span class="tp-03__tip-badge">FUNCTION</span>
            <span class="tp-03__tip-path"><span>core</span><span class="tp-03__sep">›</span><span>scheduler</span><span class="tp-03__sep">›</span><span>createScheduler</span></span>
          </span>
          <span class="tp-03__tip-sig"><span class="tp-03__kw">function</span> <span class="tp-03__fn">createScheduler</span>&lt;<span class="tp-03__var">T</span>&gt;(<br>&nbsp;&nbsp;<span class="tp-03__param">opts</span>: <span class="tp-03__var">SchedulerOptions</span>&lt;<span class="tp-03__var">T</span>&gt;<br>): <span class="tp-03__var">Queue</span>&lt;<span class="tp-03__var">T</span>&gt;</span>
          <span class="tp-03__tip-desc">Creates a back-pressured task queue with concurrency control. Items are processed in <code class="tp-03__tip-code">FIFO</code> order; failing jobs surface to the dead-letter sink.</span>
          <span class="tp-03__tip-params">
            <span class="tp-03__tip-param-row"><span class="tp-03__pname">concurrency</span><span class="tp-03__pdesc">Max parallel jobs. Default <code class="tp-03__tip-code">4</code>.</span></span>
            <span class="tp-03__tip-param-row"><span class="tp-03__pname">retries</span><span class="tp-03__pdesc">Per-task retry budget. Default <code class="tp-03__tip-code">3</code>.</span></span>
            <span class="tp-03__tip-param-row"><span class="tp-03__pname">onDrain</span><span class="tp-03__pdesc">Fires once when the queue empties.</span></span>
          </span>
          <span class="tp-03__tip-foot">
            <span>scheduler.ts · L42</span>
            <span><span class="tp-03__kbd-key">⌘</span><span class="tp-03__kbd-key">K</span> for docs</span>
          </span>
        </span></span>({</div>
        <div class="tp-03__line">&nbsp;&nbsp;<span class="tp-03__param">concurrency</span>: <span class="tp-03__num">8</span>,</div>
        <div class="tp-03__line">&nbsp;&nbsp;<span class="tp-03__param">retries</span>: <span class="tp-03__num">3</span>,</div>
        <div class="tp-03__line">});</div>
      </div>
    </div>
  </div>
</div>
```
## CSS
```css
/* 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. */

.tp-03 {
  width: 100%;
  min-height: 100vh;
  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;
}

.tp-03__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);
}

.tp-03__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;
}

.tp-03__dot {
  width: 9px;
  height: 9px;
  border-radius: 50%;
  background: #30363d;
}

.tp-03__dot:nth-child(1) {
  background: #ff5f57;
}

.tp-03__dot:nth-child(2) {
  background: #febc2e;
}

.tp-03__dot:nth-child(3) {
  background: #28c840;
}

.tp-03__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;
}

.tp-03__tab::before {
  content: '';
  width: 4px;
  height: 4px;
  border-radius: 50%;
  background: #58a6ff;
}

.tp-03__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;
}

.tp-03__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;
}

.tp-03__code {
  padding: 0 16px;
  flex: 1;
  min-width: 0;
}
/* No white-space: pre on .tp-03__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 .tp-03__symbol that nests the multi-line tooltip markup),
   breaking each token onto its own visual row in the try-it iframe. */

.tp-03__line {
}

.tp-03__kw {
  color: #ff7b72;
}

.tp-03__fn {
  color: #d2a8ff;
}

.tp-03__str {
  color: #a5d6ff;
}

.tp-03__num {
  color: #79c0ff;
}

.tp-03__cmt {
  color: #8b949e;
  font-style: italic;
}

.tp-03__var {
  color: #79c0ff;
}

.tp-03__param {
  color: #ffa657;
}

.tp-03__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;
}

.tp-03__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;
}

.tp-03__symbol:hover {
  border-color: #58a6ff;
  background: rgba(88, 166, 255, 0.08);
}

.tp-03__symbol:hover::after {
  color: #58a6ff;
}

.tp-03__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 .tp-03__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;
}

.tp-03__symbol:hover .tp-03__tip {
  opacity: 1;
  visibility: visible;
  transform: translateY(0);
  transition-delay: 0s;
}

.tp-03__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);
}

.tp-03__tip-head {
  padding: 10px 14px 9px;
  border-bottom: 1px solid #21262d;
  display: flex;
  align-items: center;
  gap: 10px;
  font-size: 11px;
}

.tp-03__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;
}

.tp-03__tip-path {
  color: #8b949e;
  font-size: 11px;
}

.tp-03__sep {
  color: #484f58;
  margin: 0 4px;
}

.tp-03__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 .tp-03__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;
}

.tp-03__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;
}

.tp-03__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;
}

.tp-03__tip-params {
  padding: 10px 14px;
  border-bottom: 1px solid #21262d;
  display: block;
}

.tp-03__tip-param-row {
  display: grid;
  grid-template-columns: 90px 1fr;
  gap: 10px;
  font-size: 11.5px;
  padding: 3px 0;
  line-height: 1.5;
}

.tp-03__pname {
  color: #ffa657;
}

.tp-03__pdesc {
  color: #8b949e;
  font-family: 'Inter', system-ui, sans-serif;
  font-size: 11.5px;
}

.tp-03__tip-foot {
  padding: 9px 14px;
  font-size: 10px;
  color: #6e7681;
  display: flex;
  justify-content: space-between;
  align-items: center;
}

.tp-03__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;
}
```

How this works

The stage recreates a syntax-highlighted editor at rest — .tp-03__editor is a 560px card on GitHub-dark canvas (#0d1117), framed by a #30363d border and a 32px tab strip with three traffic dots (#ff5f57, #febc2e, #28c840) and an orchestrator.ts tab marked by a 4px blue dot. Inside .tp-03__body a 44px gutter carries line numbers in #484f58, and .tp-03__code holds the code column at 13px on a 22px line-height — a pixel line-height, not em-based, because the source comment records that mixing em line-height with two different font-sizes drifted the gutter and code out of sync by roughly 2px per row.

The trigger is .tp-03__symbol wrapping createScheduler. It advertises itself with a dashed underline (1px dashed rgba(88, 166, 255, 0.55)) and a small info glyph appended via ::after — a two-cue discovery affordance the source explicitly documents, replacing an earlier transparent-until-hover version that failed as a discovery pattern. On hover the underline saturates to #58a6ff and the token gains a translucent blue wash.

The tooltip is a 380px stacked card pinned to bottom: calc(100% + 14px); left: -20px, with a #1c2128 to #161b22 vertical gradient and a triple-shadow stack that includes a 1px inner blue rim for JetBrains-style depth. Content is stacked in five zones separated by #21262d rules: a header with a FUNCTION badge and a chevron-separated namespace path (core, scheduler, createScheduler); a monospaced signature that keeps white-space: pre so the multi-line generic renders indented; an Inter-set prose description with an inline .tp-03__tip-code token; a two-column .tp-03__tip-param-row grid (90px 1fr) listing concurrency, retries, onDrain in #ffa657 parameter orange; and a footer that shows the source path plus a keycap-styled Command-K docs shortcut. Reveal transitions opacity, transform (translateY(8px) to 0) and a delayed visibility over 220ms.

Make it yours

  • For a light-theme editor flip .tp-03__editor to #ffffff, borders to #d0d7de, code to #24292f, gutter to #8c959f, and swap .tp-03__kw to #cf222e, .tp-03__fn to #8250df, .tp-03__str to #0a3069 — GitHub-light drops in cleanly.
  • For a JetBrains Fleet look raise the tooltip radius to 8px, add a 2px accent bar (box-shadow: inset 3px 0 0 #58a6ff), and turn the FUNCTION badge into a filled chip in #58a6ff with white text — the five-zone anatomy still holds.
  • Swap JetBrains Mono for Fira Code, Cascadia Code or Berkeley Mono without adjusting sizes — those faces share a 0.6em x-height so 13px code and 12px signature still ride the 22px baseline. Keep Inter on .tp-03__tip-desc; monospacing prose is a legibility loss.
  • Cursor / Zed dark variant: canvas to #191a1f, editor to #1e1f26, param orange to #d19a66, badge to #a377ff on rgba(163, 119, 255, 0.15). Keep the underline dashed — its discovery role is what makes the demo self-teaching.
  • Tailwind arbitrary-value map: bg-[#0d1117] border border-[#30363d] rounded-lg for the shell; text-[#c9d1d9] font-mono text-[13px] leading-[22px] for the code column; underline decoration-dashed decoration-[#58a6ff]/55 underline-offset-2 for the symbol trigger; grid grid-cols-[90px_1fr] gap-[10px] for the param rows.

Gotchas — read before shipping

  • The dashed underline plus info glyph is a real accessibility win over the earlier transparent-until-hover version, but it only satisfies SC 3.3.5 Help in Context if the tooltip is reachable by keyboard. Add tabindex="0", role="button", aria-describedby pointing to the tip's id, and mirror :hover with :focus-visible — otherwise SC 2.1.1 Keyboard fails outright.
  • The info glyph is content injected via ::after, which most screen readers announce as decorative. If the info dot conveys the only discovery cue, move it into the DOM as a real span sibling with aria-hidden="true" and give the symbol an accessible name via aria-label such as createScheduler, function, hover or focus for signature.
  • SC 1.4.11 Non-text Contrast on the underline: rgba(88, 166, 255, 0.55) against #161b22 resolves near 3:1 — right at the threshold. Raise alpha to 0.7 to stay safely above 3:1 for the dashed line, and verify the info dot at 0.6 alpha the same way.
  • The tooltip is 380px wide anchored at left: -20px. On viewport widths below 620px the tip clips off the editor's left rule. Add a @media (max-width: 640px) block that repositions to left: 0; right: 0; width: auto; max-width: 92vw and disables the ::after pointer, or portal the tooltip to document.body with a Popover API primitive.
  • Whitespace handling is fragile — .tp-03__tip resets white-space: normal to prevent source newlines between spans from leaking as visible whitespace, and .tp-03__tip-sig re-enables pre for the multi-line signature. SSR frameworks that minify HTML with whitespace-preserving heuristics (Nuxt renderJitless, Astro's compress plugin under certain configs) can strip the intentional line breaks and collapse the signature to one line.
  • The tooltip inherits overflow: hidden from ancestor cards in production layouts (sidebars, panel systems). The source comment documents that the demo container carefully avoids overflow clipping; downstream you will need overflow: visible on the editor shell, or the Popover API with popover="auto" to escape the stacking context entirely. Popover is Chrome 114+, Safari 17+, Firefox 125+.

Browser support

ChromeSafariFirefoxEdge
88+14.1+89+88+

the current implementation is pure CSS with no experimental APIs; if you migrate to Popover API to escape overflow parents you inherit Chrome 114+, Safari 17+, Firefox 125+ baselines.

Techniques used in this demo

Search CodeFronts

Loading…