21 CSS Tooltips09 / 21

Pure CSSMIT licensed

Team Avatar Card

Initials-only avatar circles in a row that lift slightly on hover and pop out a full profile card — gradient hero band, larger avatar with shadow ring, role + presence dot + bio + three-stat row + Message / View Profile buttons. Built for team rosters, mention popovers, comment hovers.

Published

Live Demo
Try it

The code

<div class="tp-09">
  <div class="tp-09__inner">
    <div class="tp-09__t">
      <div class="tp-09__circle" style="background:linear-gradient(135deg,#6028a0,#9060d0)">SL</div>
      <div class="tp-09__tip">
        <div class="tp-09__hero" style="background:linear-gradient(135deg,#4a1880,#8050c0)">
          <div class="tp-09__photo" style="background:linear-gradient(135deg,#6028a0,#9060d0)">SL</div>
        </div>
        <div class="tp-09__body">
          <div class="tp-09__name">Sarah Lindqvist</div>
          <div class="tp-09__role">CEO · Executive</div>
          <div class="tp-09__online"><span class="tp-09__online-dot" style="background:#22c55e"></span>Active now</div>
          <div class="tp-09__bio">Former Stripe PM. Building the next layer of financial infrastructure for SMBs.</div>
          <div class="tp-09__stats">
            <div><div class="tp-09__stat-n">214</div><div class="tp-09__stat-l">Team</div></div>
            <div><div class="tp-09__stat-n">8yr</div><div class="tp-09__stat-l">Tenure</div></div>
            <div><div class="tp-09__stat-n">$1.2B</div><div class="tp-09__stat-l">Valuation</div></div>
          </div>
        </div>
        <div class="tp-09__foot">
          <div class="tp-09__btn">Message</div>
          <div class="tp-09__btn tp-09__btn-prim">View Profile</div>
        </div>
      </div>
    </div>

    <div class="tp-09__t">
      <div class="tp-09__circle" style="background:linear-gradient(135deg,#204080,#4070c0)">MW</div>
      <div class="tp-09__tip">
        <div class="tp-09__hero" style="background:linear-gradient(135deg,#102060,#3060a0)">
          <div class="tp-09__photo" style="background:linear-gradient(135deg,#204080,#4070c0)">MW</div>
        </div>
        <div class="tp-09__body">
          <div class="tp-09__name">Marcus Webb</div>
          <div class="tp-09__role">CTO · Engineering</div>
          <div class="tp-09__online"><span class="tp-09__online-dot" style="background:#22c55e"></span>Active now</div>
          <div class="tp-09__bio">Ex-Google infra. Obsessed with distributed systems and making things fast at scale.</div>
          <div class="tp-09__stats">
            <div><div class="tp-09__stat-n">96</div><div class="tp-09__stat-l">Team</div></div>
            <div><div class="tp-09__stat-n">5yr</div><div class="tp-09__stat-l">Tenure</div></div>
            <div><div class="tp-09__stat-n">340</div><div class="tp-09__stat-l">PRs</div></div>
          </div>
        </div>
        <div class="tp-09__foot">
          <div class="tp-09__btn">Message</div>
          <div class="tp-09__btn tp-09__btn-prim">View Profile</div>
        </div>
      </div>
    </div>

    <div class="tp-09__t">
      <div class="tp-09__circle" style="background:linear-gradient(135deg,#0a6050,#1aa080)">ER</div>
      <div class="tp-09__tip">
        <div class="tp-09__hero" style="background:linear-gradient(135deg,#054030,#108060)">
          <div class="tp-09__photo" style="background:linear-gradient(135deg,#0a6050,#1aa080)">ER</div>
        </div>
        <div class="tp-09__body">
          <div class="tp-09__name">Elena Russo</div>
          <div class="tp-09__role">CMO · Marketing</div>
          <div class="tp-09__online"><span class="tp-09__online-dot" style="background:#f59e0b"></span>Away · 2h ago</div>
          <div class="tp-09__bio">Brand strategist. Grew Notion's Europe presence 3×. London-based, GMT+1.</div>
          <div class="tp-09__stats">
            <div><div class="tp-09__stat-n">68</div><div class="tp-09__stat-l">Team</div></div>
            <div><div class="tp-09__stat-n">3yr</div><div class="tp-09__stat-l">Tenure</div></div>
            <div><div class="tp-09__stat-n">+84%</div><div class="tp-09__stat-l">YoY</div></div>
          </div>
        </div>
        <div class="tp-09__foot">
          <div class="tp-09__btn">Message</div>
          <div class="tp-09__btn tp-09__btn-prim">View Profile</div>
        </div>
      </div>
    </div>

    <div class="tp-09__t">
      <div class="tp-09__circle" style="background:linear-gradient(135deg,#802050,#c04080)">AP</div>
      <div class="tp-09__tip">
        <div class="tp-09__hero" style="background:linear-gradient(135deg,#601030,#a03060)">
          <div class="tp-09__photo" style="background:linear-gradient(135deg,#802050,#c04080)">AP</div>
        </div>
        <div class="tp-09__body">
          <div class="tp-09__name">Aisha Patel</div>
          <div class="tp-09__role">Growth Lead · Marketing</div>
          <div class="tp-09__online"><span class="tp-09__online-dot" style="background:#94a3b8"></span>Offline</div>
          <div class="tp-09__bio">Data-driven growth. 0 → 100k users in 18 months. Runs the acquisition engine.</div>
          <div class="tp-09__stats">
            <div><div class="tp-09__stat-n">6</div><div class="tp-09__stat-l">Team</div></div>
            <div><div class="tp-09__stat-n">1yr</div><div class="tp-09__stat-l">Tenure</div></div>
            <div><div class="tp-09__stat-n">$1.2M</div><div class="tp-09__stat-l">Budget</div></div>
          </div>
        </div>
        <div class="tp-09__foot">
          <div class="tp-09__btn">Message</div>
          <div class="tp-09__btn tp-09__btn-prim">View Profile</div>
        </div>
      </div>
    </div>
  </div>
</div>
.tp-09 {
  width: 100%;
  min-height: 100vh;
  background: #ffffff;
  /* Top room for the profile card (~280px tall) popping up from each
       avatar circle. */
  padding: 300px 28px 40px;
  display: flex;
  align-items: flex-end;
  justify-content: center;
  font-family: 'Inter', system-ui, sans-serif;
}

.tp-09__inner {
  display: flex;
  gap: 14px;
  flex-wrap: wrap;
  justify-content: center;
}

.tp-09__t {
  position: relative;
  cursor: help;
}

.tp-09__circle {
  width: 48px;
  height: 48px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 13px;
  font-weight: 600;
  color: rgba(255, 255, 255, 0.9);
  letter-spacing: 0.03em;
  transition: transform .15s, box-shadow .15s;
}

.tp-09__t:hover .tp-09__circle {
  transform: scale(1.1) translateY(-3px);
  box-shadow: 0 8px 20px rgba(0, 0, 0, 0.18);
}

.tp-09__tip {
  position: absolute;
  bottom: calc(100% + 12px);
  left: 50%;
  transform: translateX(-50%) translateY(4px);
  width: 240px;
  background: #fff;
  border-radius: 12px;
  border: 1px solid rgba(0, 0, 0, 0.07);
  box-shadow: 0 12px 36px rgba(0, 0, 0, 0.12), 0 2px 8px rgba(0, 0, 0, 0.06);
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  transition: opacity .25s, transform .25s cubic-bezier(0.22, 1, 0.36, 1), visibility 0s linear .25s;
  z-index: 30;
  overflow: hidden;
  text-align: left;
}

.tp-09__t:hover .tp-09__tip {
  opacity: 1;
  visibility: visible;
  transform: translateX(-50%) translateY(0);
  transition-delay: 0s;
}

.tp-09__hero {
  height: 56px;
  position: relative;
}

.tp-09__photo {
  width: 48px;
  height: 48px;
  border-radius: 50%;
  border: 3px solid #fff;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.14);
  position: absolute;
  left: 14px;
  bottom: -14px;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 14px;
  font-weight: 700;
  color: rgba(255, 255, 255, 0.92);
}

.tp-09__body {
  padding: 20px 14px 12px;
}

.tp-09__name {
  font-size: 14px;
  font-weight: 600;
  color: #111;
}

.tp-09__role {
  font-size: 10.5px;
  color: #888;
  margin-top: 2px;
}

.tp-09__online {
  display: flex;
  align-items: center;
  gap: 5px;
  font-size: 10px;
  color: #888;
  margin-top: 6px;
}

.tp-09__online-dot {
  width: 6px;
  height: 6px;
  border-radius: 50%;
}

.tp-09__bio {
  font-size: 11.5px;
  color: #555;
  margin-top: 8px;
  line-height: 1.5;
}

.tp-09__stats {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  margin-top: 12px;
  padding-top: 11px;
  border-top: 1px solid #f0f0f0;
  text-align: center;
}

.tp-09__stat-n {
  font-size: 15px;
  font-weight: 700;
  color: #111;
}

.tp-09__stat-l {
  font-size: 8px;
  color: #bbb;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  margin-top: 1px;
}

.tp-09__foot {
  padding: 9px 14px;
  border-top: 1px solid #f0f0f0;
  display: flex;
  gap: 6px;
}

.tp-09__btn {
  flex: 1;
  padding: 6px 0;
  border-radius: 6px;
  font-size: 11px;
  font-weight: 500;
  text-align: center;
  border: 1px solid #e8e8e8;
  color: #444;
  background: #fafafa;
  cursor: pointer;
}

.tp-09__btn-prim {
  background: #111;
  border-color: #111;
  color: #fff;
}
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: Team Avatar Card
Source: https://codefronts.com/snippets/css-tooltips/team-avatar-card/

Initials-only avatar circles in a row that lift slightly on hover and pop out a full profile card — gradient hero band, larger avatar with shadow ring, role + presence dot + bio + three-stat row + Message / View Profile buttons. Built for team rosters, mention popovers, comment hovers.
## HTML
```html
<div class="tp-09">
  <div class="tp-09__inner">
    <div class="tp-09__t">
      <div class="tp-09__circle" style="background:linear-gradient(135deg,#6028a0,#9060d0)">SL</div>
      <div class="tp-09__tip">
        <div class="tp-09__hero" style="background:linear-gradient(135deg,#4a1880,#8050c0)">
          <div class="tp-09__photo" style="background:linear-gradient(135deg,#6028a0,#9060d0)">SL</div>
        </div>
        <div class="tp-09__body">
          <div class="tp-09__name">Sarah Lindqvist</div>
          <div class="tp-09__role">CEO · Executive</div>
          <div class="tp-09__online"><span class="tp-09__online-dot" style="background:#22c55e"></span>Active now</div>
          <div class="tp-09__bio">Former Stripe PM. Building the next layer of financial infrastructure for SMBs.</div>
          <div class="tp-09__stats">
            <div><div class="tp-09__stat-n">214</div><div class="tp-09__stat-l">Team</div></div>
            <div><div class="tp-09__stat-n">8yr</div><div class="tp-09__stat-l">Tenure</div></div>
            <div><div class="tp-09__stat-n">$1.2B</div><div class="tp-09__stat-l">Valuation</div></div>
          </div>
        </div>
        <div class="tp-09__foot">
          <div class="tp-09__btn">Message</div>
          <div class="tp-09__btn tp-09__btn-prim">View Profile</div>
        </div>
      </div>
    </div>

    <div class="tp-09__t">
      <div class="tp-09__circle" style="background:linear-gradient(135deg,#204080,#4070c0)">MW</div>
      <div class="tp-09__tip">
        <div class="tp-09__hero" style="background:linear-gradient(135deg,#102060,#3060a0)">
          <div class="tp-09__photo" style="background:linear-gradient(135deg,#204080,#4070c0)">MW</div>
        </div>
        <div class="tp-09__body">
          <div class="tp-09__name">Marcus Webb</div>
          <div class="tp-09__role">CTO · Engineering</div>
          <div class="tp-09__online"><span class="tp-09__online-dot" style="background:#22c55e"></span>Active now</div>
          <div class="tp-09__bio">Ex-Google infra. Obsessed with distributed systems and making things fast at scale.</div>
          <div class="tp-09__stats">
            <div><div class="tp-09__stat-n">96</div><div class="tp-09__stat-l">Team</div></div>
            <div><div class="tp-09__stat-n">5yr</div><div class="tp-09__stat-l">Tenure</div></div>
            <div><div class="tp-09__stat-n">340</div><div class="tp-09__stat-l">PRs</div></div>
          </div>
        </div>
        <div class="tp-09__foot">
          <div class="tp-09__btn">Message</div>
          <div class="tp-09__btn tp-09__btn-prim">View Profile</div>
        </div>
      </div>
    </div>

    <div class="tp-09__t">
      <div class="tp-09__circle" style="background:linear-gradient(135deg,#0a6050,#1aa080)">ER</div>
      <div class="tp-09__tip">
        <div class="tp-09__hero" style="background:linear-gradient(135deg,#054030,#108060)">
          <div class="tp-09__photo" style="background:linear-gradient(135deg,#0a6050,#1aa080)">ER</div>
        </div>
        <div class="tp-09__body">
          <div class="tp-09__name">Elena Russo</div>
          <div class="tp-09__role">CMO · Marketing</div>
          <div class="tp-09__online"><span class="tp-09__online-dot" style="background:#f59e0b"></span>Away · 2h ago</div>
          <div class="tp-09__bio">Brand strategist. Grew Notion's Europe presence 3×. London-based, GMT+1.</div>
          <div class="tp-09__stats">
            <div><div class="tp-09__stat-n">68</div><div class="tp-09__stat-l">Team</div></div>
            <div><div class="tp-09__stat-n">3yr</div><div class="tp-09__stat-l">Tenure</div></div>
            <div><div class="tp-09__stat-n">+84%</div><div class="tp-09__stat-l">YoY</div></div>
          </div>
        </div>
        <div class="tp-09__foot">
          <div class="tp-09__btn">Message</div>
          <div class="tp-09__btn tp-09__btn-prim">View Profile</div>
        </div>
      </div>
    </div>

    <div class="tp-09__t">
      <div class="tp-09__circle" style="background:linear-gradient(135deg,#802050,#c04080)">AP</div>
      <div class="tp-09__tip">
        <div class="tp-09__hero" style="background:linear-gradient(135deg,#601030,#a03060)">
          <div class="tp-09__photo" style="background:linear-gradient(135deg,#802050,#c04080)">AP</div>
        </div>
        <div class="tp-09__body">
          <div class="tp-09__name">Aisha Patel</div>
          <div class="tp-09__role">Growth Lead · Marketing</div>
          <div class="tp-09__online"><span class="tp-09__online-dot" style="background:#94a3b8"></span>Offline</div>
          <div class="tp-09__bio">Data-driven growth. 0 → 100k users in 18 months. Runs the acquisition engine.</div>
          <div class="tp-09__stats">
            <div><div class="tp-09__stat-n">6</div><div class="tp-09__stat-l">Team</div></div>
            <div><div class="tp-09__stat-n">1yr</div><div class="tp-09__stat-l">Tenure</div></div>
            <div><div class="tp-09__stat-n">$1.2M</div><div class="tp-09__stat-l">Budget</div></div>
          </div>
        </div>
        <div class="tp-09__foot">
          <div class="tp-09__btn">Message</div>
          <div class="tp-09__btn tp-09__btn-prim">View Profile</div>
        </div>
      </div>
    </div>
  </div>
</div>
```
## CSS
```css
.tp-09 {
  width: 100%;
  min-height: 100vh;
  background: #ffffff;
  /* Top room for the profile card (~280px tall) popping up from each
       avatar circle. */
  padding: 300px 28px 40px;
  display: flex;
  align-items: flex-end;
  justify-content: center;
  font-family: 'Inter', system-ui, sans-serif;
}

.tp-09__inner {
  display: flex;
  gap: 14px;
  flex-wrap: wrap;
  justify-content: center;
}

.tp-09__t {
  position: relative;
  cursor: help;
}

.tp-09__circle {
  width: 48px;
  height: 48px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 13px;
  font-weight: 600;
  color: rgba(255, 255, 255, 0.9);
  letter-spacing: 0.03em;
  transition: transform .15s, box-shadow .15s;
}

.tp-09__t:hover .tp-09__circle {
  transform: scale(1.1) translateY(-3px);
  box-shadow: 0 8px 20px rgba(0, 0, 0, 0.18);
}

.tp-09__tip {
  position: absolute;
  bottom: calc(100% + 12px);
  left: 50%;
  transform: translateX(-50%) translateY(4px);
  width: 240px;
  background: #fff;
  border-radius: 12px;
  border: 1px solid rgba(0, 0, 0, 0.07);
  box-shadow: 0 12px 36px rgba(0, 0, 0, 0.12), 0 2px 8px rgba(0, 0, 0, 0.06);
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  transition: opacity .25s, transform .25s cubic-bezier(0.22, 1, 0.36, 1), visibility 0s linear .25s;
  z-index: 30;
  overflow: hidden;
  text-align: left;
}

.tp-09__t:hover .tp-09__tip {
  opacity: 1;
  visibility: visible;
  transform: translateX(-50%) translateY(0);
  transition-delay: 0s;
}

.tp-09__hero {
  height: 56px;
  position: relative;
}

.tp-09__photo {
  width: 48px;
  height: 48px;
  border-radius: 50%;
  border: 3px solid #fff;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.14);
  position: absolute;
  left: 14px;
  bottom: -14px;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 14px;
  font-weight: 700;
  color: rgba(255, 255, 255, 0.92);
}

.tp-09__body {
  padding: 20px 14px 12px;
}

.tp-09__name {
  font-size: 14px;
  font-weight: 600;
  color: #111;
}

.tp-09__role {
  font-size: 10.5px;
  color: #888;
  margin-top: 2px;
}

.tp-09__online {
  display: flex;
  align-items: center;
  gap: 5px;
  font-size: 10px;
  color: #888;
  margin-top: 6px;
}

.tp-09__online-dot {
  width: 6px;
  height: 6px;
  border-radius: 50%;
}

.tp-09__bio {
  font-size: 11.5px;
  color: #555;
  margin-top: 8px;
  line-height: 1.5;
}

.tp-09__stats {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  margin-top: 12px;
  padding-top: 11px;
  border-top: 1px solid #f0f0f0;
  text-align: center;
}

.tp-09__stat-n {
  font-size: 15px;
  font-weight: 700;
  color: #111;
}

.tp-09__stat-l {
  font-size: 8px;
  color: #bbb;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  margin-top: 1px;
}

.tp-09__foot {
  padding: 9px 14px;
  border-top: 1px solid #f0f0f0;
  display: flex;
  gap: 6px;
}

.tp-09__btn {
  flex: 1;
  padding: 6px 0;
  border-radius: 6px;
  font-size: 11px;
  font-weight: 500;
  text-align: center;
  border: 1px solid #e8e8e8;
  color: #444;
  background: #fafafa;
  cursor: pointer;
}

.tp-09__btn-prim {
  background: #111;
  border-color: #111;
  color: #fff;
}
```

How this works

The roster is a wrap-friendly flex row of .tp-09__t triggers, each one an initials-only 48px circle drawn from a per-person linear-gradient(135deg, …). The letters sit at 13px Inter 600 in rgba(255, 255, 255, 0.9), letter-spaced 0.03em — the typographic register of a modern team directory such as Read.cv, Linear, or a Notion mention popover. On hover the circle lifts through a transform: scale(1.1) translateY(-3px) paired with a soft 0 8px 20px rgba(0, 0, 0, 0.18) shadow, the physical tell that the avatar is now an actionable surface.

The profile card .tp-09__tip opens above the avatar as a 240px card with a 12px radius and a two-layer shadow. It leads with a 56px .tp-09__hero gradient band — a darker echo of the avatar's own gradient so the two feel siblings — behind which a larger 48px .tp-09__photo rests half-below the band with a 3px white border. Below the hero, a 20px-padded body sets the display name at 14px 600, the role at 10.5px #888, a presence line with a 6px status dot (green active, amber away, slate offline), a 11.5px bio in #555, and a three-column stat grid with 15px 700 numerals over 8px uppercase labels.

The footer holds two buttons — a neutral Message and a filled black View Profile — each a 6px-radius flex-1 pill. Reveal transitions opacity, visibility, and transform through cubic-bezier(0.22, 1, 0.36, 1) at 250ms so the card lands with a light glide rather than a snap. Hand-coded, copy-paste, no dependencies, WCAG-conscious, framework-agnostic, scoped under .tp-09.

Make it yours

  • Replace the initials with real portrait photography by swapping the inner text of .tp-09__circle and .tp-09__photo for a background-image or an inline img element; keep the 48px dimensions and 50% border-radius so the geometry stays consistent with the initials version.
  • Add a role-level accent by driving the hero band gradient from a CSS custom property such as --role-hue set per person on .tp-09__t; engineering leads read cool blues, marketing warm magentas, without duplicating the gradient stack per row.
  • Swap the presence dot for a live indicator by binding .tp-09__online-dot's background to a data attribute such as data-status="active|away|offline|dnd" and adding an attribute-selector rule per state; the label text updates in the same node without a component rerender.
  • Extend the stat row from three to four cells by changing grid-template-columns: repeat(3, 1fr) to repeat(4, 1fr) and reducing the numeral font-size from 15px to 13px; the label typography holds shape at any column count because the padding is fixed on the parent.
  • Convert the buttons into router links by promoting .tp-09__btn from a div to an anchor; keep the flex-1 sizing so the two links share the footer evenly, and add a :focus-visible ring at outline: 2px solid #111 with outline-offset: 2px.

Gotchas — read before shipping

  • The card sits above the avatar at bottom: calc(100% + 12px). In a top-anchored roster (nav bar, sidebar header) the card renders behind the viewport edge. Reserve at least 300px of top padding on the containing surface or migrate to Anchor Positioning with position-try-fallbacks: flip-block so the card drops below when space above is tight.
  • Buttons are semantically div elements here for demo brevity. Ship them as button or anchor elements, add type="button" where forms are nearby, and include an accessible name — the visual label is fine but screen readers need a matching text node (WCAG SC 4.1.2).
  • The button hit target is roughly 28px tall — below the 44 × 44 CSS pixel guidance in WCAG SC 2.5.5. On touch surfaces users will mis-tap, especially with two side-by-side buttons at 6px gap. Increase padding to 10px vertical on touch breakpoints or expand the click area with an ::after overlay.
  • The initials pattern encodes identity through color alone. Two people whose initials collide (SL, SL) can only be told apart by hue. Add a title attribute, an aria-label, or a visible fallback name so the roster is navigable without color perception (WCAG SC 1.4.1).
  • The hover-only reveal locks out keyboard users. Add tabindex="0" to each .tp-09__t and extend the reveal selector to .tp-09__t:hover, .tp-09__t:focus-within. Also add an ESC-to-dismiss handler if you promote the card to the Popover API — otherwise focus is trapped in the card once opened via keyboard (WCAG SC 2.1.1, 2.1.2).
  • The 240px card contains six text blocks. Screen-reader announcement is verbose — every focus of the avatar reads out name, role, presence, bio, three stats, and two buttons. Wrap the card in role="tooltip" for a read-once summary and expose the full profile via an aria-details link, so power users can drill in without hearing the full manifest every time.

Browser support

ChromeSafariFirefoxEdge
88+14+78+88+

Flexbox, CSS Grid, and standard easings all ship in the baseline. Border-radius on gradient backgrounds renders identically. If you upgrade to the native Popover API for click-to-open behaviour, ship popovertarget behind @supports selector(:popover-open) to keep the hover fallback for older engines.

Techniques used in this demo

Search CodeFronts

Loading…