21 CSS Tooltips17 / 21

Pure CSSMIT licensed

Photo EXIF Grid

A dark contact-sheet grid of eight gradient photographs. Hover any cell to lift a developer-tools-style card with full EXIF metadata — camera body, lens, aperture, shutter, ISO, focal length, white balance, and location. For photo portfolios and stock catalogs.

Published

Live Demo
Try it

The code

<div class="tp-17">
  <div class="tp-17__grid">
    <div class="tp-17__cell">
      <div class="tp-17__img" style="background:linear-gradient(160deg,#c86020 0%,#e8a030 40%,#f0c060 70%,#a04010 100%)"></div>
      <div class="tp-17__tip">
        <div class="tp-17__tip-head"><div><div class="tp-17__camera">Sony A7 IV</div><div class="tp-17__lens">FE 24-70mm f/2.8 GM</div></div><div class="tp-17__date">Mar 14, 2024</div></div>
        <div class="tp-17__settings">
          <div class="tp-17__set"><div class="tp-17__key">Aperture</div><div class="tp-17__val">f/8.0</div></div>
          <div class="tp-17__set"><div class="tp-17__key">Shutter</div><div class="tp-17__val">1/250s</div></div>
          <div class="tp-17__set"><div class="tp-17__key">ISO</div><div class="tp-17__val">100</div></div>
        </div>
        <div class="tp-17__extra"><span>Focal: <strong>35mm</strong></span><span>WB: <strong>Daylight</strong></span><span>Malibu, CA</span></div>
      </div>
    </div>
    <div class="tp-17__cell">
      <div class="tp-17__img" style="background:linear-gradient(145deg,#1a1218 0%,#2c1828 50%,#180c14 100%)"></div>
      <div class="tp-17__tip">
        <div class="tp-17__tip-head"><div><div class="tp-17__camera">Nikon Z6 III</div><div class="tp-17__lens">NIKKOR Z 85mm f/1.8 S</div></div><div class="tp-17__date">Jan 8, 2024</div></div>
        <div class="tp-17__settings">
          <div class="tp-17__set"><div class="tp-17__key">Aperture</div><div class="tp-17__val">f/1.8</div></div>
          <div class="tp-17__set"><div class="tp-17__key">Shutter</div><div class="tp-17__val">1/160s</div></div>
          <div class="tp-17__set"><div class="tp-17__key">ISO</div><div class="tp-17__val">800</div></div>
        </div>
        <div class="tp-17__extra"><span>Focal: <strong>85mm</strong></span><span>WB: <strong>Tungsten</strong></span><span>Flash: <strong>Off</strong></span></div>
      </div>
    </div>
    <div class="tp-17__cell">
      <div class="tp-17__img" style="background:linear-gradient(180deg,#082840 0%,#0c4060 35%,#1060a0 70%,#082040 100%)"></div>
      <div class="tp-17__tip">
        <div class="tp-17__tip-head"><div><div class="tp-17__camera">Fujifilm X-T5</div><div class="tp-17__lens">XF 16-80mm f/4 R</div></div><div class="tp-17__date">Feb 22, 2024</div></div>
        <div class="tp-17__settings">
          <div class="tp-17__set"><div class="tp-17__key">Aperture</div><div class="tp-17__val">f/11</div></div>
          <div class="tp-17__set"><div class="tp-17__key">Shutter</div><div class="tp-17__val">1/500s</div></div>
          <div class="tp-17__set"><div class="tp-17__key">ISO</div><div class="tp-17__val">200</div></div>
        </div>
        <div class="tp-17__extra"><span>Focal: <strong>16mm</strong></span><span>WB: <strong>Cloudy</strong></span><span>Big Sur, CA</span></div>
      </div>
    </div>
    <div class="tp-17__cell">
      <div class="tp-17__img" style="background:linear-gradient(135deg,#0c1e10 0%,#1a3818 40%,#2a5a28 70%,#0c2010 100%)"></div>
      <div class="tp-17__tip">
        <div class="tp-17__tip-head"><div><div class="tp-17__camera">Canon EOS R5</div><div class="tp-17__lens">RF 24-105mm f/4L IS</div></div><div class="tp-17__date">Apr 5, 2024</div></div>
        <div class="tp-17__settings">
          <div class="tp-17__set"><div class="tp-17__key">Aperture</div><div class="tp-17__val">f/5.6</div></div>
          <div class="tp-17__set"><div class="tp-17__key">Shutter</div><div class="tp-17__val">1/60s</div></div>
          <div class="tp-17__set"><div class="tp-17__key">ISO</div><div class="tp-17__val">400</div></div>
        </div>
        <div class="tp-17__extra"><span>Focal: <strong>50mm</strong></span><span>WB: <strong>Shade</strong></span><span>Olympic NP</span></div>
      </div>
    </div>
    <div class="tp-17__cell">
      <div class="tp-17__img" style="background:linear-gradient(160deg,#080c18 0%,#101428 40%,#181c30 70%,#0c0e20 100%)"></div>
      <div class="tp-17__tip">
        <div class="tp-17__tip-head"><div><div class="tp-17__camera">Sony A7 IV</div><div class="tp-17__lens">FE 50mm f/1.4 GM</div></div><div class="tp-17__date">Dec 18, 2023</div></div>
        <div class="tp-17__settings">
          <div class="tp-17__set"><div class="tp-17__key">Aperture</div><div class="tp-17__val">f/2.0</div></div>
          <div class="tp-17__set"><div class="tp-17__key">Shutter</div><div class="tp-17__val">1/30s</div></div>
          <div class="tp-17__set"><div class="tp-17__key">ISO</div><div class="tp-17__val">3200</div></div>
        </div>
        <div class="tp-17__extra"><span>Focal: <strong>50mm</strong></span><span>WB: <strong>Auto</strong></span><span>Tokyo, JP</span></div>
      </div>
    </div>
    <div class="tp-17__cell">
      <div class="tp-17__img" style="background:linear-gradient(155deg,#604020 0%,#d09050 40%,#f0c880 70%,#806030 100%)"></div>
      <div class="tp-17__tip">
        <div class="tp-17__tip-head"><div><div class="tp-17__camera">Hasselblad X2D</div><div class="tp-17__lens">XCD 45mm f/3.5</div></div><div class="tp-17__date">Nov 3, 2023</div></div>
        <div class="tp-17__settings">
          <div class="tp-17__set"><div class="tp-17__key">Aperture</div><div class="tp-17__val">f/16</div></div>
          <div class="tp-17__set"><div class="tp-17__key">Shutter</div><div class="tp-17__val">1/1000s</div></div>
          <div class="tp-17__set"><div class="tp-17__key">ISO</div><div class="tp-17__val">64</div></div>
        </div>
        <div class="tp-17__extra"><span>Focal: <strong>45mm</strong></span><span>WB: <strong>Daylight</strong></span><span>Sahara, Morocco</span></div>
      </div>
    </div>
    <div class="tp-17__cell">
      <div class="tp-17__img" style="background:linear-gradient(170deg,#c8c8cc 0%,#a8a8b0 40%,#d0d0d4 70%,#b0b0b8 100%)"></div>
      <div class="tp-17__tip">
        <div class="tp-17__tip-head"><div><div class="tp-17__camera">Leica M11</div><div class="tp-17__lens">Summicron 50mm f/2</div></div><div class="tp-17__date">Oct 14, 2023</div></div>
        <div class="tp-17__settings">
          <div class="tp-17__set"><div class="tp-17__key">Aperture</div><div class="tp-17__val">f/4.0</div></div>
          <div class="tp-17__set"><div class="tp-17__key">Shutter</div><div class="tp-17__val">1/125s</div></div>
          <div class="tp-17__set"><div class="tp-17__key">ISO</div><div class="tp-17__val">200</div></div>
        </div>
        <div class="tp-17__extra"><span>Focal: <strong>50mm</strong></span><span>WB: <strong>Cloudy</strong></span><span>Highlands, UK</span></div>
      </div>
    </div>
    <div class="tp-17__cell">
      <div class="tp-17__img" style="background:linear-gradient(150deg,#200830 0%,#401060 40%,#602080 65%,#100420 100%)"></div>
      <div class="tp-17__tip">
        <div class="tp-17__tip-head"><div><div class="tp-17__camera">Nikon Z6 III</div><div class="tp-17__lens">NIKKOR Z 14-30mm f/4 S</div></div><div class="tp-17__date">Sep 27, 2023</div></div>
        <div class="tp-17__settings">
          <div class="tp-17__set"><div class="tp-17__key">Aperture</div><div class="tp-17__val">f/2.8</div></div>
          <div class="tp-17__set"><div class="tp-17__key">Shutter</div><div class="tp-17__val">20s</div></div>
          <div class="tp-17__set"><div class="tp-17__key">ISO</div><div class="tp-17__val">1600</div></div>
        </div>
        <div class="tp-17__extra"><span>Focal: <strong>14mm</strong></span><span>WB: <strong>3200K</strong></span><span>Iceland</span></div>
      </div>
    </div>
  </div>
</div>
.tp-17 {
  min-height: 100vh;
  background: #080808;
  /* Top room — EXIF cards pop UP from each photo, ~180px tall. */
  padding: 220px 28px 40px;
  font-family: 'JetBrains Mono', ui-monospace, monospace;
  /* Force full width of the .cf-stage flex parent. Without this, the
       stage collapses to its content's min-width and the 4-column grid
       inside resolves each 1fr track to ~0px, stacking all 8 photos as
       one thin vertical strip. */
  width: 100%;
  box-sizing: border-box;
}

.tp-17__grid {
  max-width: 600px;
  margin: 0 auto;
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 3px;
}

.tp-17__cell {
  position: relative;
  aspect-ratio: 3 / 2;
  cursor: help;
}

.tp-17__img {
  width: 100%;
  height: 100%;
  display: block;
  transition: filter .2s;
}

.tp-17__cell:hover .tp-17__img {
  filter: brightness(1.1);
}

.tp-17__tip {
  position: absolute;
  bottom: calc(100% + 8px);
  left: 50%;
  transform: translateX(-50%) translateY(-6px);
  z-index: 100;
  width: 230px;
  background: #111;
  border: 1px solid rgba(255, 255, 255, 0.06);
  border-radius: 6px;
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  transition: opacity .2s, transform .2s cubic-bezier(0.22, 1, 0.36, 1), visibility 0s linear .2s;
  box-shadow: 0 16px 44px rgba(0, 0, 0, 0.98);
  overflow: hidden;
  text-align: left;
}

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

.tp-17__tip-head {
  padding: 11px 13px;
  border-bottom: 1px solid rgba(255, 255, 255, 0.05);
  display: flex;
  justify-content: space-between;
  align-items: flex-end;
}

.tp-17__camera {
  font-size: 10.5px;
  color: rgba(255, 255, 255, 0.7);
}

.tp-17__lens {
  font-size: 8.5px;
  color: rgba(255, 255, 255, 0.2);
  margin-top: 2px;
}

.tp-17__date {
  font-size: 8.5px;
  color: rgba(255, 255, 255, 0.2);
}

.tp-17__settings {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  border-bottom: 1px solid rgba(255, 255, 255, 0.05);
}

.tp-17__set {
  padding: 9px 13px;
  border-right: 1px solid rgba(255, 255, 255, 0.04);
}

.tp-17__set:last-child {
  border-right: none;
}

.tp-17__key {
  font-size: 6.5px;
  color: rgba(255, 255, 255, 0.2);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  margin-bottom: 2px;
}

.tp-17__val {
  font-size: 10.5px;
  color: rgba(255, 255, 255, 0.65);
}

.tp-17__extra {
  display: flex;
  justify-content: space-between;
  padding: 8px 13px;
  font-size: 8.5px;
  color: rgba(255, 255, 255, 0.25);
  gap: 6px;
}

.tp-17__extra strong {
  color: rgba(255, 255, 255, 0.45);
  font-weight: 500;
}
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: Photo EXIF Grid
Source: https://codefronts.com/snippets/css-tooltips/photo-exif-grid/

A dark contact-sheet grid of eight gradient photographs. Hover any cell to lift a developer-tools-style card with full EXIF metadata — camera body, lens, aperture, shutter, ISO, focal length, white balance, and location. For photo portfolios and stock catalogs.
## HTML
```html
<div class="tp-17">
  <div class="tp-17__grid">
    <div class="tp-17__cell">
      <div class="tp-17__img" style="background:linear-gradient(160deg,#c86020 0%,#e8a030 40%,#f0c060 70%,#a04010 100%)"></div>
      <div class="tp-17__tip">
        <div class="tp-17__tip-head"><div><div class="tp-17__camera">Sony A7 IV</div><div class="tp-17__lens">FE 24-70mm f/2.8 GM</div></div><div class="tp-17__date">Mar 14, 2024</div></div>
        <div class="tp-17__settings">
          <div class="tp-17__set"><div class="tp-17__key">Aperture</div><div class="tp-17__val">f/8.0</div></div>
          <div class="tp-17__set"><div class="tp-17__key">Shutter</div><div class="tp-17__val">1/250s</div></div>
          <div class="tp-17__set"><div class="tp-17__key">ISO</div><div class="tp-17__val">100</div></div>
        </div>
        <div class="tp-17__extra"><span>Focal: <strong>35mm</strong></span><span>WB: <strong>Daylight</strong></span><span>Malibu, CA</span></div>
      </div>
    </div>
    <div class="tp-17__cell">
      <div class="tp-17__img" style="background:linear-gradient(145deg,#1a1218 0%,#2c1828 50%,#180c14 100%)"></div>
      <div class="tp-17__tip">
        <div class="tp-17__tip-head"><div><div class="tp-17__camera">Nikon Z6 III</div><div class="tp-17__lens">NIKKOR Z 85mm f/1.8 S</div></div><div class="tp-17__date">Jan 8, 2024</div></div>
        <div class="tp-17__settings">
          <div class="tp-17__set"><div class="tp-17__key">Aperture</div><div class="tp-17__val">f/1.8</div></div>
          <div class="tp-17__set"><div class="tp-17__key">Shutter</div><div class="tp-17__val">1/160s</div></div>
          <div class="tp-17__set"><div class="tp-17__key">ISO</div><div class="tp-17__val">800</div></div>
        </div>
        <div class="tp-17__extra"><span>Focal: <strong>85mm</strong></span><span>WB: <strong>Tungsten</strong></span><span>Flash: <strong>Off</strong></span></div>
      </div>
    </div>
    <div class="tp-17__cell">
      <div class="tp-17__img" style="background:linear-gradient(180deg,#082840 0%,#0c4060 35%,#1060a0 70%,#082040 100%)"></div>
      <div class="tp-17__tip">
        <div class="tp-17__tip-head"><div><div class="tp-17__camera">Fujifilm X-T5</div><div class="tp-17__lens">XF 16-80mm f/4 R</div></div><div class="tp-17__date">Feb 22, 2024</div></div>
        <div class="tp-17__settings">
          <div class="tp-17__set"><div class="tp-17__key">Aperture</div><div class="tp-17__val">f/11</div></div>
          <div class="tp-17__set"><div class="tp-17__key">Shutter</div><div class="tp-17__val">1/500s</div></div>
          <div class="tp-17__set"><div class="tp-17__key">ISO</div><div class="tp-17__val">200</div></div>
        </div>
        <div class="tp-17__extra"><span>Focal: <strong>16mm</strong></span><span>WB: <strong>Cloudy</strong></span><span>Big Sur, CA</span></div>
      </div>
    </div>
    <div class="tp-17__cell">
      <div class="tp-17__img" style="background:linear-gradient(135deg,#0c1e10 0%,#1a3818 40%,#2a5a28 70%,#0c2010 100%)"></div>
      <div class="tp-17__tip">
        <div class="tp-17__tip-head"><div><div class="tp-17__camera">Canon EOS R5</div><div class="tp-17__lens">RF 24-105mm f/4L IS</div></div><div class="tp-17__date">Apr 5, 2024</div></div>
        <div class="tp-17__settings">
          <div class="tp-17__set"><div class="tp-17__key">Aperture</div><div class="tp-17__val">f/5.6</div></div>
          <div class="tp-17__set"><div class="tp-17__key">Shutter</div><div class="tp-17__val">1/60s</div></div>
          <div class="tp-17__set"><div class="tp-17__key">ISO</div><div class="tp-17__val">400</div></div>
        </div>
        <div class="tp-17__extra"><span>Focal: <strong>50mm</strong></span><span>WB: <strong>Shade</strong></span><span>Olympic NP</span></div>
      </div>
    </div>
    <div class="tp-17__cell">
      <div class="tp-17__img" style="background:linear-gradient(160deg,#080c18 0%,#101428 40%,#181c30 70%,#0c0e20 100%)"></div>
      <div class="tp-17__tip">
        <div class="tp-17__tip-head"><div><div class="tp-17__camera">Sony A7 IV</div><div class="tp-17__lens">FE 50mm f/1.4 GM</div></div><div class="tp-17__date">Dec 18, 2023</div></div>
        <div class="tp-17__settings">
          <div class="tp-17__set"><div class="tp-17__key">Aperture</div><div class="tp-17__val">f/2.0</div></div>
          <div class="tp-17__set"><div class="tp-17__key">Shutter</div><div class="tp-17__val">1/30s</div></div>
          <div class="tp-17__set"><div class="tp-17__key">ISO</div><div class="tp-17__val">3200</div></div>
        </div>
        <div class="tp-17__extra"><span>Focal: <strong>50mm</strong></span><span>WB: <strong>Auto</strong></span><span>Tokyo, JP</span></div>
      </div>
    </div>
    <div class="tp-17__cell">
      <div class="tp-17__img" style="background:linear-gradient(155deg,#604020 0%,#d09050 40%,#f0c880 70%,#806030 100%)"></div>
      <div class="tp-17__tip">
        <div class="tp-17__tip-head"><div><div class="tp-17__camera">Hasselblad X2D</div><div class="tp-17__lens">XCD 45mm f/3.5</div></div><div class="tp-17__date">Nov 3, 2023</div></div>
        <div class="tp-17__settings">
          <div class="tp-17__set"><div class="tp-17__key">Aperture</div><div class="tp-17__val">f/16</div></div>
          <div class="tp-17__set"><div class="tp-17__key">Shutter</div><div class="tp-17__val">1/1000s</div></div>
          <div class="tp-17__set"><div class="tp-17__key">ISO</div><div class="tp-17__val">64</div></div>
        </div>
        <div class="tp-17__extra"><span>Focal: <strong>45mm</strong></span><span>WB: <strong>Daylight</strong></span><span>Sahara, Morocco</span></div>
      </div>
    </div>
    <div class="tp-17__cell">
      <div class="tp-17__img" style="background:linear-gradient(170deg,#c8c8cc 0%,#a8a8b0 40%,#d0d0d4 70%,#b0b0b8 100%)"></div>
      <div class="tp-17__tip">
        <div class="tp-17__tip-head"><div><div class="tp-17__camera">Leica M11</div><div class="tp-17__lens">Summicron 50mm f/2</div></div><div class="tp-17__date">Oct 14, 2023</div></div>
        <div class="tp-17__settings">
          <div class="tp-17__set"><div class="tp-17__key">Aperture</div><div class="tp-17__val">f/4.0</div></div>
          <div class="tp-17__set"><div class="tp-17__key">Shutter</div><div class="tp-17__val">1/125s</div></div>
          <div class="tp-17__set"><div class="tp-17__key">ISO</div><div class="tp-17__val">200</div></div>
        </div>
        <div class="tp-17__extra"><span>Focal: <strong>50mm</strong></span><span>WB: <strong>Cloudy</strong></span><span>Highlands, UK</span></div>
      </div>
    </div>
    <div class="tp-17__cell">
      <div class="tp-17__img" style="background:linear-gradient(150deg,#200830 0%,#401060 40%,#602080 65%,#100420 100%)"></div>
      <div class="tp-17__tip">
        <div class="tp-17__tip-head"><div><div class="tp-17__camera">Nikon Z6 III</div><div class="tp-17__lens">NIKKOR Z 14-30mm f/4 S</div></div><div class="tp-17__date">Sep 27, 2023</div></div>
        <div class="tp-17__settings">
          <div class="tp-17__set"><div class="tp-17__key">Aperture</div><div class="tp-17__val">f/2.8</div></div>
          <div class="tp-17__set"><div class="tp-17__key">Shutter</div><div class="tp-17__val">20s</div></div>
          <div class="tp-17__set"><div class="tp-17__key">ISO</div><div class="tp-17__val">1600</div></div>
        </div>
        <div class="tp-17__extra"><span>Focal: <strong>14mm</strong></span><span>WB: <strong>3200K</strong></span><span>Iceland</span></div>
      </div>
    </div>
  </div>
</div>
```
## CSS
```css
.tp-17 {
  min-height: 100vh;
  background: #080808;
  /* Top room — EXIF cards pop UP from each photo, ~180px tall. */
  padding: 220px 28px 40px;
  font-family: 'JetBrains Mono', ui-monospace, monospace;
  /* Force full width of the .cf-stage flex parent. Without this, the
       stage collapses to its content's min-width and the 4-column grid
       inside resolves each 1fr track to ~0px, stacking all 8 photos as
       one thin vertical strip. */
  width: 100%;
  box-sizing: border-box;
}

.tp-17__grid {
  max-width: 600px;
  margin: 0 auto;
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 3px;
}

.tp-17__cell {
  position: relative;
  aspect-ratio: 3 / 2;
  cursor: help;
}

.tp-17__img {
  width: 100%;
  height: 100%;
  display: block;
  transition: filter .2s;
}

.tp-17__cell:hover .tp-17__img {
  filter: brightness(1.1);
}

.tp-17__tip {
  position: absolute;
  bottom: calc(100% + 8px);
  left: 50%;
  transform: translateX(-50%) translateY(-6px);
  z-index: 100;
  width: 230px;
  background: #111;
  border: 1px solid rgba(255, 255, 255, 0.06);
  border-radius: 6px;
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  transition: opacity .2s, transform .2s cubic-bezier(0.22, 1, 0.36, 1), visibility 0s linear .2s;
  box-shadow: 0 16px 44px rgba(0, 0, 0, 0.98);
  overflow: hidden;
  text-align: left;
}

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

.tp-17__tip-head {
  padding: 11px 13px;
  border-bottom: 1px solid rgba(255, 255, 255, 0.05);
  display: flex;
  justify-content: space-between;
  align-items: flex-end;
}

.tp-17__camera {
  font-size: 10.5px;
  color: rgba(255, 255, 255, 0.7);
}

.tp-17__lens {
  font-size: 8.5px;
  color: rgba(255, 255, 255, 0.2);
  margin-top: 2px;
}

.tp-17__date {
  font-size: 8.5px;
  color: rgba(255, 255, 255, 0.2);
}

.tp-17__settings {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  border-bottom: 1px solid rgba(255, 255, 255, 0.05);
}

.tp-17__set {
  padding: 9px 13px;
  border-right: 1px solid rgba(255, 255, 255, 0.04);
}

.tp-17__set:last-child {
  border-right: none;
}

.tp-17__key {
  font-size: 6.5px;
  color: rgba(255, 255, 255, 0.2);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  margin-bottom: 2px;
}

.tp-17__val {
  font-size: 10.5px;
  color: rgba(255, 255, 255, 0.65);
}

.tp-17__extra {
  display: flex;
  justify-content: space-between;
  padding: 8px 13px;
  font-size: 8.5px;
  color: rgba(255, 255, 255, 0.25);
  gap: 6px;
}

.tp-17__extra strong {
  color: rgba(255, 255, 255, 0.45);
  font-weight: 500;
}
```

How this works

The tp-17 component is a contact-sheet CSS grid: tp-17__grid declares grid-template-columns: repeat(4, 1fr) with a 3px gutter, and each tp-17__cell holds its aspect-ratio: 3 / 2 shape so eight photos tile predictably at any container width. The photos themselves are pure gradient thumbnails on tp-17__img, meaning the demo ships zero raster assets and stays under a single-digit KB. A filter: brightness(1.1) transition on hover signals the active cell without moving the layout.

Each metadata card is tp-17__tip, a 230px absolutely positioned panel anchored bottom: calc(100% + 8px) above its cell and horizontally centred. The card carries a monospaced Lightroom-style rhythm: tp-17__tip-head pairs body and lens on the left (tp-17__camera at 10.5px, tp-17__lens at 8.5px) with the capture date on the right, on top of a 1px separator. The transition sequence is opacity, translate-Y, and delayed visibility — the same recipe every Lightroom-style panel uses to keep pointer targets alive during exit.

The technical block underneath is tp-17__settings, a three-column grid of tp-17__set cells for aperture, shutter, and ISO. Column separators come from border-right with :last-child zeroing the trailing edge, and the labels use 6.5px uppercase 0.1em tracking on tp-17__key with 10.5px readable values on tp-17__val. The tp-17__extra footer flexes focal length, white balance, flash state, and location across the bottom, with strong tags accented to rgba(255,255,255,0.45) so numbers pop against the label prose.

Make it yours

  • Retint by editing the eight inline gradients on tp-17__img. Because the rest of the card holds a neutral #111 background, you can swap the gradients for real img tags with object-fit: cover and the tooltip stays untouched.
  • Swap the 4-column grid to 3 or 5 by changing the repeat(4, 1fr) line on tp-17__grid. The aspect-ratio: 3 / 2 on each cell keeps every photo the same proportion regardless of the column count.
  • Move metadata below the photo by flipping the tooltip anchor: replace bottom: calc(100% + 8px) with top: calc(100% + 8px) on tp-17__tip. Keep the translateX centring untouched so the horizontal alignment survives.
  • Add a lens-mount pill by inserting a span alongside tp-17__lens and reusing the tp-17__key uppercase treatment. The 3-column tp-17__settings grid can expand to 4 columns without touching the border-right rules.
  • Replace JetBrains Mono with IBM Plex Mono or Berkeley Mono for a tighter EXIF panel; keep the 6.5px key label tracking at 0.1em so the uppercase treatment survives the metric change.

Gotchas — read before shipping

  • The 220px top padding on the demo container exists because the 180px tooltip pops UP from the first row. If you embed tp-17 inside a page with only 40px of top padding, the topmost card will render behind the header. Preserve the padding or add a tp-17__tip--below variant for the first row.
  • Column tracks collapse when the container has no width. The comment in the source calls this out: without width: 100% on tp-17, a flex parent shrinks it to intrinsic min-width and the four 1fr tracks resolve to zero. Keep the box-sizing: border-box and explicit 100% or the grid folds into a strip.
  • The metadata card uses 6.5px labels and 8.5px lens text on translucent white. That reads elegant, but 6.5px text falls under SC 1.4.4 Resize Text expectations only if it scales with rem. Convert the type stack to rem-based values (e.g. 0.4375rem) before shipping so 200% zoom still resolves cleanly.
  • cursor: help is the only affordance on the cell, and hover-only reveal fails keyboard access under SC 2.1.1. Promote each tp-17__cell to a button, link, or add tabindex="0", then mirror the :hover .tp-17__tip selector with :focus-within.
  • Firefox on Windows tends to render the diagonal gradients with visible banding at low colour depth. If your users report banding, replace the gradients with a 1x1 svg radial filter or a tiny grain PNG mask; the tooltip logic is unaffected.
  • The tp-17__extra footer uses display: flex; justify-content: space-between with three spans of variable length. When a location string like "Sahara, Morocco" beats the flex track, the whitespace collapses unpredictably in Safari. Add min-width: 0 on the spans and let the middle item truncate with ellipsis.

Browser support

ChromeSafariFirefoxEdge
90+15.4+89+90+

aspect-ratio, subgrid-adjacent CSS grid, and the visibility transition delay are all native. Safari 15.4+ is the floor because aspect-ratio shipped for iOS in that release. Below 15.4, cells collapse to zero height; fall back to a padding-bottom hack if you have to support 14.x.

Techniques used in this demo

Search CodeFronts

Loading…