Placeholder shimmer animation

Placeholder shimmer animation

by Csaba Hellinger

HTML

<div id="app"></div>

CSS

body {
  background: #111;
  padding: 3vw;
  font-family: Helvetica, sans-serif;
}

.item {
  border: 1px solid #fff;
  color: #fff;
  padding: 4vw;
  margin-bottom: 1vw;
  display: grid;
  grid-template-areas: 
    "img title"
    "img sub"
    "img .";
  grid-template-columns: min-content 1fr;
  grid-template-rows: min-content;
}

.item-image {
  grid-area: img;
  background: pink;
  width: 15vw;
  height: 15vw;
  margin-right: 4vw;
}

.item-title {
  grid-area: title;
  margin-bottom: 1vw;
  font-size: 1.2rem;
  font-weight: 700;
}

.item-sub {
  grid-area: sub;
}

React

const LOAD_DELAY = 6000;

const people = [
  { id: 1, name: 'Gipsz Jakab', job: 'Ablakjavító' },
  { id: 2, name: 'Óh Pál', job: 'Drágaköves' },
  { id: 3, name: 'Óh Teréz', job: 'Színesfém gyűjtő' }
];

const Item = ({ title, sub, isLoaded }) => {
  if (!isLoaded) {
    return (
      <svg viewBox="0 0 100 25">
        <defs>
          <mask id="mask" x="0" y="0" width="100" height="30">
            <rect x="0" y="0" width="100" height="25" stroke="#fff" fill="#000" />
            <rect x="5" y="5" width="15" height="15" fill="#fff" />
            <rect x="25" y="5" width="25" height="5" fill="#fff" />
            <rect x="25" y="12" width="45" height="3" fill="#fff" />
          </mask>
          <linearGradient id="gradient" x1="99%" y1="59%" x2="1%" y2="41%">
            <stop offset="0%" stopColor="#fff1" />
            <stop offset="35%" stopColor="#fff1" />
            <stop offset="50%" stopColor="#fff2" />
            <stop offset="65%" stopColor="#fff1" />
            <stop offset="100%" stopColor="#fff1" />
            <animateTransform attributeName="gradientTransform"
              type="translate"
              from="-1 0"
              to="1 0"
              begin="0s"
              dur="2s"
              repeatCount="indefinite"/>
          </linearGradient>          
        </defs>
        <rect x="0" y="0" width="100" height="30" fill="url(#gradient)" stroke="none" mask="url(#mask)" />    
      </svg>
    );
  }
  return (
    <div className="item">
      <div className="item-image"></div>
      <div className="item-title">{title}</div>
      <div className="item-sub">{sub}</div>      
    </div>
  );
};

const App = () => {
  const [isLoaded, setLoaded] = React.useState(false);
  
  React.useEffect(() => {
    setTimeout(() => setLoaded(true), LOAD_DELAY);  
  },[]);
  
  return (
    <React.Fragment>
      {people.map(person => 
        <Item 
          key={person.id}
          title={person.name}
          sub={person.job}
         ...