← Mike Higgins Home
Custom Code Experiment •

Robohash API

Source Code View HTML / CSS / JavaScript
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Robohash API</title>
  <link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet">
  <style>
    * {
      margin: 0;
      padding: 0;
      box-sizing: border-box;
    }

    body {
      position: relative;
      font-family: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
        Oxygen, Ubuntu, Cantarell, sans-serif;
      color: #e0e0e0;
      line-height: 1.6;
      min-height: 100vh;
      padding: 0;
      background: #000;
    }

    body::before {
      content: "";
      position: fixed;
      top: 0;
      left: 0;
      width: 100%;
      height: 100%;
      background: linear-gradient(45deg, #0a0a0a 0%, #1a1a1a 100%);
      z-index: -1;
    }

    .container {
      position: relative;
      z-index: 1;
      max-width: 1200px;
      margin: 0 auto;
      padding: 2rem 1.5rem;
    }

    header {
      text-align: left;
      margin-bottom: 3rem;
      padding-top: 1rem;
    }

    h1 {
      font-size: 2.5rem;
      font-weight: 700;
      background: linear-gradient(135deg, #fff 0%, #a0a0a0 100%);
      -webkit-background-clip: text;
      -webkit-text-fill-color: transparent;
      margin-bottom: 0.5rem;
    }

    .subtitle {
      font-size: 1.1rem;
      font-weight: 400;
      color: #888;
      margin-bottom: 2rem;
      max-width: 500px;
      line-height: 1.6;
    }

    #controls {
      display: flex;
      justify-content: flex-start;
      margin-bottom: 2.5rem;
    }

    #controls button {
      padding: 1rem 2.5rem;
      font-size: 1rem;
      font-weight: 600;
      border: 2px solid #fff;
      border-radius: 12px;
      background: #000;
      color: #fff;
      cursor: pointer;
      transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
      box-shadow: 0 4px 20px rgba(255, 255, 255, 0.1);
      position: relative;
      overflow: hidden;
      letter-spacing: 0.5px;
      text-transform: uppercase;
    }

    #controls button:hover {
      background: #fff;
      color: #000;
      transform: translateY(-2px);
      box-shadow: 0 8px 30px rgba(255, 255, 255, 0.2);
    }

    #controls button:active {
      transform: translateY(0);
      transition: all 0.1s ease;
    }

    #controls button::before {
      content: "";
      position: absolute;
      top: 50%;
      left: 50%;
      width: 0;
      height: 0;
      border-radius: 50%;
      background: rgba(255, 255, 255, 0.1);
      transform: translate(-50%, -50%);
      transition: width 0.6s, height 0.6s;
    }

    #controls button:hover::before {
      width: 300px;
      height: 300px;
    }

    #images {
      display: grid;
      grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
      gap: 1.5rem;
      margin-bottom: 3rem;
      justify-items: center;
    }

    .placeholder {
      width: 100%;
      aspect-ratio: 1;
      background: #1a1a1a;
      border-radius: 16px;
      display: flex;
      align-items: center;
      justify-content: center;
      box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3);
      overflow: hidden;
      position: relative;
      transition: transform 0.3s ease, box-shadow 0.3s ease;
      border: 1px solid #333;
    }

    .placeholder:hover {
      transform: translateY(-8px) scale(1.02);
      box-shadow: 0 12px 40px rgba(0, 0, 0, 0.4);
      border-color: #666;
    }

    .spinner {
      width: 36px;
      height: 36px;
      border: 3px solid rgba(255, 255, 255, 0.1);
      border-top: 3px solid #fff;
      border-radius: 50%;
      animation: spin 1s linear infinite;
    }

    .placeholder img {
      width: 100%;
      height: 100%;
      object-fit: cover;
      border-radius: 16px;
      transition: transform 0.5s ease;
    }

    .placeholder:hover img {
      transform: scale(1.05);
    }

    @keyframes spin {
      0% {
        transform: rotate(0deg);
      }
      100% {
        transform: rotate(360deg);
      }
    }

    .footer-links {
      display: flex;
      justify-content: flex-start;
      gap: 1rem;
      margin-top: 2rem;
      flex-wrap: wrap;
    }

    .footer-link {
      display: inline-flex;
      align-items: center;
      gap: 0.5rem;
      padding: 0.75rem 1.5rem;
      font-size: 0.9rem;
      font-weight: 600;
      text-decoration: none;
      border: 2px solid #fff;
      border-radius: 10px;
      background: #000;
      color: #fff;
      cursor: pointer;
      transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
      box-shadow: 0 2px 15px rgba(255, 255, 255, 0.1);
      position: relative;
      overflow: hidden;
      letter-spacing: 0.3px;
      text-transform: uppercase;
    }

    .footer-link:hover {
      background: #fff;
      color: #000;
      transform: translateY(-2px);
      box-shadow: 0 6px 25px rgba(255, 255, 255, 0.2);
    }

    .footer-link:active {
      transform: translateY(0);
    }

    .footer-link::before {
      content: "";
      position: absolute;
      top: 50%;
      left: 50%;
      width: 0;
      height: 0;
      border-radius: 50%;
      background: rgba(255, 255, 255, 0.1);
      transform: translate(-50%, -50%);
      transition: width 0.6s, height 0.6s;
    }

    .footer-link:hover::before {
      width: 200px;
      height: 200px;
    }

    .repo-link {
      display: inline-flex;
      align-items: center;
      gap: 0.5rem;
      color: #fff;
      text-decoration: none;
      font-weight: 500;
      padding: 0.75rem 1.5rem;
      border-radius: 10px;
      transition: all 0.3s ease;
      background: rgba(255, 255, 255, 0.05);
      border: 1px solid rgba(255, 255, 255, 0.1);
      margin-bottom: 2rem;
    }

    .repo-link:hover {
      background: rgba(255, 255, 255, 0.1);
      transform: translateY(-2px);
      border-color: rgba(255, 255, 255, 0.2);
      box-shadow: 0 8px 25px rgba(255, 255, 255, 0.1);
    }

    @media (max-width: 768px) {
      #images {
        grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
        gap: 1rem;
      }

      .container {
        padding: 1.5rem 1rem;
      }

      #controls {
        justify-content: center;
      }

      #controls button {
        width: 100%;
        max-width: 300px;
      }

      .footer-links {
        justify-content: center;
        gap: 0.75rem;
      }

      h1 {
        font-size: 2rem;
      }

      .subtitle {
        font-size: 1rem;
      }
    }

    @media (max-width: 480px) {
      #images {
        grid-template-columns: repeat(2, 1fr);
      }

      .footer-links {
        flex-direction: column;
        align-items: stretch;
        gap: 0.75rem;
      }

      header {
        text-align: center;
      }

      #controls {
        justify-content: center;
      }

      .footer-link {
        width: 100%;
        justify-content: center;
      }

      .repo-link {
        justify-content: center;
      }
    }

    @keyframes fadeIn {
      from {
        opacity: 0;
        transform: translateY(20px);
      }
      to {
        opacity: 1;
        transform: translateY(0);
      }
    }

    .placeholder {
      animation: fadeIn 0.5s ease forwards;
    }
  </style>
</head>

<body>
  <div class="container">
    <header>
      <p class="subtitle">Generate unique robot avatars instantly. Each robot is created from a hash of random data, ensuring no two are alike.</p>
    </header>

    <main>
      <div id="controls">
        <button onclick="generateImages()" class="btn-primary">Generate New Robots</button>
      </div>
      <div id="images"></div>
    </main>

    <footer>
      <div class="footer-links">
        <a href="https://robohash.org/" target="_blank" rel="noopener noreferrer" class="footer-link">
          <svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
            <path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 15l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z" />
          </svg>
          Powered by Robohash.org
        </a>
      </div>
    </footer>
  </div>

  <script>
    function getRandomNumber() {
      return Math.floor(Math.random() * 1000000);
    }

    function generateImages() {
      const imageContainer = document.getElementById("images");
      imageContainer.innerHTML = "";
      const totalImages = 10;

      for (let i = 0; i < totalImages; i++) {
        const placeholder = document.createElement("div");
        placeholder.classList.add("placeholder");
        placeholder.style.animationDelay = `${i * 0.05}s`;

        const spinner = document.createElement("div");
        spinner.classList.add("spinner");
        placeholder.appendChild(spinner);

        imageContainer.appendChild(placeholder);

        const img = new Image();
        img.src = `https://robohash.org/set_set2/bgset_bg1/${getRandomNumber()}?size=300x300`;
        img.alt = `Robohash Image ${i + 1}`;

        img.onload = () => {
          placeholder.innerHTML = "";
          placeholder.appendChild(img);
        };

        img.onerror = () => {
          placeholder.innerHTML = "⚠️ Error loading";
          placeholder.style.color = "#ff6b6b";
          placeholder.style.fontWeight = "500";
          placeholder.style.display = "flex";
          placeholder.style.alignItems = "center";
          placeholder.style.justifyContent = "center";
        };
      }
    }

    generateImages();
  </script>
</body>

</html>