Feature discovery, click through

by mnk

HTML

<div class="card" id="card">
  <div class="hint">Click the button through the squircle hole</div>

  <button id="underButton" class="under-button">Click me through the hole</button>

  <svg id="overlaySvg" class="overlay-svg" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false">
    <path id="overlayPath" d="" />
  </svg>
</div>

CSS

:root{
    --card-w: 100%;
    --card-h: 260px;
  }

  body {
    margin: 32px;
    display: grid;
    place-items: start center;
    font-family: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial;
    background: #f3f4f6;
  }

  .card {
    width: var(--card-w);
    height: var(--card-h);
    position: relative;
    background: linear-gradient(135deg,#3b82f6,#06b6d4);
    color: white;
    display: flex;
    align-items: center;
    justify-content: center;
    overflow: visible;
    border-radius: 12px;
    box-shadow: 0 8px 30px rgba(2,6,23,0.25);
    user-select: none;
  }

  /* Example element underneath the hole */
  .under-button {
    position: relative;      /* keep above card background */
    z-index: 0;
    padding: 12px 18px;
    font-size: 16px;
    background: rgba(255,255,255,0.12);
    border: 1px solid rgba(255,255,255,0.12);
    color: white;
    border-radius: 8px;
  }

  /* SVG overlay sits on top of the card */
  .overlay-svg {
    position: absolute;
    inset: 0;               /* fill the card exactly */
    width: 100%;
    height: 100%;
    pointer-events: none;   /* let the internal painted shape decide hit-testing */
  }

  /* The path is the painted black overlay (outer rect minus squircle). Only painted pixels receive pointer events */
  #overlayPath {
    fill: rgba(0,0,0,0.5);
    fill-rule: evenodd;    /* the inner squircle subpath will become a hole */
    pointer-events: auto;  /* painted pixels of this path catch pointer events; the hole does not */
  }

  /* small UI hint */
  .hint {
    position: absolute;
    left: 12px;
    top: 12px;
    color: rgba(255,255,255,0.9);
    font-size: 13px;
    z-index: 2;
    user-select: none;
    pointer-events: none;
  }

JavaScript

function superellipsePath(cx, cy, a, b, n, steps) {
  let d = '';
  for (let i = 0; i <= steps; i++) {
    const t = (i / steps) * Math.PI * 2;
    const c = Math.cos(t), s = Math.sin(t);
    const x = cx + a * Math.sign(c) * Math.pow(Math.abs(c), 2 / n);
    const y = cy + b * Math.sign(s) * Math.pow(Math.abs(s), 2 / n);
    d += (i === 0 ? 'M ' : ' L ') + x.toFixed(3) + ' ' + y.toFixed(3);
  }
  d += ' Z';
  return d;
}

const card = document.getElementById('card');
const svg = document.getElementById('overlaySvg');
const pathEl = document.getElementById('overlayPath');

function updateOverlay() {
  const rect = card.getBoundingClientRect();
  const w = Math.max(1, Math.round(rect.width));
  const h = Math.max(1, Math.round(rect.height));

  // Keep SVG coordinate space in pixels so path can be built in userSpaceOnUse.
  svg.setAttribute('viewBox', `0 0 ${w} ${h}`);
  svg.setAttribute('preserveAspectRatio', 'none');

  // outer rectangle (clockwise)
  const outer = `M 0 0 H ${w} V ${h} H 0 Z`;

  // squircle params (change these to taste)
  const cx = w / 2;
  const cy = h / 2;
  const holeDiameter = Math.min(w, h) * 0.36; // adjust percent for larger/smaller hole
  const a = holeDiameter / 2;
  const b = holeDiameter / 2;
  const exponent = 4; // 4 -> typical squircle; 2 -> circle; >4 -> squarer
  const steps = Math.max(64, Math.round(Math.max(a, b) * 1.2)); // adapt smoothness to size

  const inner = superellipsePath(cx, cy, a, b, exponent, steps);

  // combine: outer followed by inner. fill-rule="evenodd" on the path makes inner a hole.
  pathEl.setAttribute('d', outer + ' ' + inner);
}

// On the overlay path, stop pointer events from bubbling to the card.
// Using pointer events covers mouse/touch/stylus in modern browsers.
function blockClicksOnOverlay() {
  const pathEl = document.getElementById('overlayPath');

  // Prevent pointer events from reaching the card/parent
 ...