<h1>The spinning hexagonal click-spot show</h1>

<p>A furtherance to a click animation I saw a while back over on codrops.<br>Rewritten to remove library dependencies, added spin, et voila&hellip;</p>


<div class=grd_parent>
  <div class=grd-2>

    <article class=grd_itm id=a1>
      <a class=grd_lnk-small data-spot href="#a1">
        <div class=grd_copy>Click me (link)</div>
      </a>
    </article>

    <article class=grd_itm>
      <button class=grd_btn-small data-spot>
        <div class=grd_copy>Click me (button)</div>
      </button>
    </article>

    <article class=grd_itm id=a2>
      <a class=grd_lnk data-spot href="#a2">
        <div class=grd_copy>Click me (link)</div>
      </a>
    </article>

    <article class=grd_itm>
      <button class=grd_btn data-spot>
        <div class=grd_copy>Click me (button)</div>
      </button>
    </article>

  </div>
</div>

<p>As used on on my latest <a class=lnk title="[new window]" target=_blank href="https://websemantics.uk/">homepage</a> update.</p>



<svg style=display:none>
  <defs>
    <!-- method and matrix lifted from codrops (lost reference to original) -->
    <symbol id="spot-hexagon">
      <path d="M9.6 76.5L4 29 42.6.5l44 19L91.8 67 53.4 95.5l-44-19z"/>
    </symbol>
    <symbol id="spot-hexagons" viewbox="0 0 96 96">
      <use xlink:href="#spot-hexagon" opacity=".3"/>
      <use xlink:href="#spot-hexagon" opacity=".4" transform="matrix(.5 0 0 .5 25 25)"/>
      <use xlink:href="#spot-hexagon" opacity=".5" transform="matrix(.3 0 0 .3 36.3 36.3)"/>
    </symbol>
  </defs>
</svg>


<p>Not finding the original reference bugs me.<br>I'm pretty certain it was on <a title="[new window]" target=_blank href="https://tympanus.net/codrops/">codrops</a>.<br>Apologies to the originator.<br>If you know where it came from please add a comment.</p>
<p>Update 29/10/2020: There's a similar version in the CSS Tricks article: <a title="[new window]" target=_blank href="https://css-tricks.com/how-to-recreate-the-ripple-effect-of-material-design-buttons/">Recreate the ripple effect of Material Design buttons</a> with a full description on how to build.</p>




[[[https://codepen.io/2kool2/pen/mKeeGM]]]
* {box-sizing: border-box;}
html {

  --textColor: hsl(269, 25%, 23%);
  --titleColor: hsl(269, 19%, 30%);

  --spacing-xlarge:   2.618rem;
  --spacing-large:    1.618rem;
  --spacing-medium:   1rem;
  --spacing-small:    0.618rem;
  --spacing-xsmall:   0.382rem;

  --fs1: 1.618rem; /* h1 */
  --fs2: 1.382rem; /* h2 */
  --fs3: 1.115rem; /* h3 */
  --fs4: 1rem; /* p */
  --fs5: .854rem; /* smaller */

  --lh1: 1.236; /* h1 */
  --lh2: 1.3; /* h2 */
  --lh3: 1.3; /* h3 */
  --lh4: 1.45; /* p */
  --lh5: 1.5; /* smaller */

  font-family: sans-serif;
  line-height: var(--lh4);
  color: #5a5a5a;
  background-color: #E9F0FB;
}
body {
  margin: 0;
  text-align: center;
}
h1 {
  font-size: var(--fs1);
  line-height: var(--lh1);
  font-weight: 100;
}
p {
  padding: var(--spacing-medium);
}
.lnk {text-decoration: none; border-bottom: 1px solid currentColor;}

/* The layout grid */

.grd_parent {
  --spacing: var(--spacing-medium);
  padding: var(--spacing);
}
[class^="grd-"] {
  --columns: 1fr;
  display: grid;
  grid-gap: var(--spacing);
  grid-template-columns: var(--columns);
}
@media (min-width: 40em) { /* 640px */
  [class^="grd-2"] {
    --columns: 1fr 1fr;
  }
}
[class^="grd_itm"] {
  border: 1px solid hsla(214, 71%, 47%, .15);
  padding: 0;
  min-width: 300px;
}


/* The link and button */
[class^="grd_btn"] {
  cursor: pointer;
  width: 100%;
  font-size: inherit;
}
[class^="grd_lnk"],
[class^="grd_btn"] {
  display: block;
  min-height: 15rem;
  position: relative;
  text-decoration: none;
  padding: 0.5rem;
  color: var(--textColor);
  border: 1px solid rgba(255,255,255,.7);
  background-color: rgba(225,255,235,.3);
  transition:
    box-shadow .3s ease,
    background-color .3s ease,
    transform .7s ease-in;
}
[class^="grd_lnk"]:hover,
[class^="grd_lnk"]:focus,
[class^="grd_btn"]:hover,
[class^="grd_btn"]:focus {
  color: inherit;
  outline: 0 solid;
  will-change: background-color, box-shadow, border-color;
  background-color: rgba(225,255,235,.6);
  box-shadow: 0 4px 4px hsla(269, 19%, 30%, .5);
  border-color: hsl(0, 0%, 99.8%);
}
[class^="grd_"][class*="-small"] {
  background-color: rgba(255,255,235,.4);
  min-height: 4rem;
}
[class^="grd_"][class*="-small"]:hover,
[class^="grd_"][class*="-small"]:focus {
  background-color: rgba(255,255,235,.6);
}



.grd_copy {
  text-transform: uppercase;
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate3d(-50%, -50%, 0);
  color: hsl(214, 71%, 45%);
}



/* Hexagonal activation spot */

[data-spot] {
  position: relative;
  overflow: hidden;
}
[data-spot] * {
  pointer-events: none;
}
.spot-hex {
  display: block;
  position: absolute;
  overflow: hidden;
  transform: rotatez(0deg) scale(0);
  opacity: 1;
}
.spot-hex use {
  fill: hsl(214, 100%, 70%);
}
.spot_parent-clicked {
  box-shadow: 0 0 0 rgba(0,0,0,0) !important;
}
.spot-hex-on {
  will-change: opacity, transform;
  animation: spot-hex-open 1s linear;
}

@keyframes spot-hex-open {
  0% {
    opacity: 1;
    transform: rotatez(0deg) scale(.1);
  }
  100% {
    opacity: 0;
    transform: rotatez(60deg) scale(3);
  }
}
var supportsES6=function(){try{return new Function('(a = 0) => a'),!0}catch(n){return!1}}();


// Hexagonal spot click animation applied to objects with the attribute data-spot
// SVG hexagonal symbol (#spot-hexagons) needs to be defined in the HTML
// All children require pointer-events: none; to activate
// Updated 11/10/2019 to fall inline with my current coding style:
// - Recalculated the centre for keyboard users
(function () {

  'use strict';
  if (!supportsES6) return false;

  const cfg = {
    boxSelect : '[data-spot]',
    spotClass : 'spot-hex',
    spotOnClass : 'spot-hex-on',
    parentClickedClass : 'spot_parent-clicked',
    symbolId : '#spot-hexagons'
  };

  const _addSpotSvg = box => {
    const NS = 'http://www.w3.org/2000/svg';
    const svg = document.createElementNS(NS, 'svg');
    const use = document.createElementNS(NS, 'use');
    use.setAttributeNS('http://www.w3.org/1999/xlink', 'xlink:href', cfg.symbolId);
    svg.appendChild(use);
    svg.classList.add(cfg.spotClass);
    box.insertBefore(svg, box.firstChild);
  };

  const _setSpotStyle = (e, box, spot) => {

    const dia = Math.max(box.clientWidth, box.clientHeight);

    let offX = e.offsetX;
    let offY = e.offsetY;

    // Trying to remove the pointer-events:none requirement, but this offset calc is too inaccurate
    // if (e.currentTarget !== e.target) {
    //   offX += e.target.offsetLeft / 2;
    //   offY += e.target.offsetTop / 2;
    // }

    // Also aligns click point to the box centre when activated via keyboard
    let X = (offX === 0 ? (box.clientWidth / 2) : offX) - (dia / 2);
    let Y = (offY === 0 ? (box.clientHeight / 2) : offY) - (dia / 2);

    spot.setAttribute('style', `top: ${Y}px; left: ${X}px; height: ${dia}px; width: ${dia}px`);
  };

  const _setSpotClasses = spot => {
    requestAnimationFrame(_ => {
      spot.classList.add(cfg.spotOnClass);
      spot.parentElement.classList.add(cfg.parentClickedClass);
      spot.addEventListener('animationend', function(e){
        spot.classList.remove(cfg.spotOnClass);
        spot.parentElement.classList.remove(cfg.parentClickedClass);
      }, {once: true});
    });
  };

  const _activateSpot = (e, box) => {

    const spot = e.currentTarget.querySelector('.' + cfg.spotClass);
    if (!spot) return false;

    // Handle multiple activations
    spot.classList.remove(cfg.spotOnClass);

    _setSpotStyle(e, box, spot);
    _setSpotClasses(spot);

  };

  const _boxClicked = (e) => {

    // REQUIRES: all (box) children must have pointer-events:none
    const activationObj = e.target.querySelector('a') ||  e.target.querySelector('button');

    _activateSpot(e, e.target);

    if (activationObj) activationObj.click();

  };

  (_ => {
    const boxes = document.querySelectorAll(cfg.boxSelect);
    for (const box of boxes) {
      _addSpotSvg(box);
      box.addEventListener('click', _boxClicked);
    }
  })();

 }());
Run Pen

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

This Pen doesn't use any external JavaScript resources.