DP
Profile card for GitHub pages

How to make a profile card with HTML/CSS/JS


Let’s make a profile card for GitHub pages that can be used as a bio link to store various contact information that can be easily shared.

Here is the code for a basic version of such card:

HTML

<!doctype html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta name="description" content="=> META DESCRIPTION OF YOUR CARD." />
    <link rel="icon" type="image/x-icon" href="=> PATH TO YOUR FAVICON" />
    <link rel="stylesheet" type="text/css" href="=> PATH TO YOUR CSS FILE" />
    <title>=> PAGE TITLE</title>
  </head>
  <body>
    <main class="profileCard-main">
      <img
        src="=> PATH TO THE MAIN IMAGE"
        alt="=> DESCRIPTION OF THE PROFILE IMAGE"
        class="profileCard-mainImg"
      />
      <section class="profileCard-section">
        <h1 class="profileCard-h1">=> YOUR NAME</h1>
        <p class="profileCard-bio">=> YOUR BIO / POSITION / PITCH / ETC.</p>
        <a
          href="=> YOUR MAIN SITE LINK"
          class="profileCard-mainLink"
          target="_blank"
          rel="noreferrer"
        >
          <img
            src="=> YOUR MAIN SITE IMAGE FOR THE LINK"
            alt="=> DESCRIPTION OF THE MAIN SITE IMAGE"
          />
        </a>
        <p class="profileCard-imgDescription">
          => IMAGE DESCRIPTION (e.g.: "Click the image to visit my site.")
        </p>
        <a
          href="=> YOUR SOCIALS LINK 1"
          class="profileCard-socialLink"
          target="_blank"
          rel="noreferrer"
        >
          => YOUR SOCIALS LINK 1 NAME
        </a>
        <a
          href="=> YOUR SOCIALS LINK 1"
          class="profileCard-socialLink"
          target="_blank"
          rel="noreferrer"
        >
          => YOUR SOCIALS LINK 2 NAME
        </a>
        <button class="profileCard-btn" onclick="copyLink()">
          Copy profile link
        </button>
      </section>
    </main>
    <script src="PATH TO YOUR JAVASCRIPT FILE"></script>
  </body>
</html>

CSS

:root {
  /* Change colors for each part/element here */
  --backgroundPrimary: #000814;
  --backgroundSecondary: #1d2d44;
  --text: #333;
  --cardBackground: #fff;
  --socialLinkBackground: #e9ecef;
  --socialLinkHoverBackground: #f8f9fa;
  --socialLinkHover: #007bff;
  --btnBorder: #e9ecef;
  --btnHoverBackground: #f8f9fa;
  --btnCopiedBackground: #d4edda;
  --btnCopied: #155724;
}

* {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

body {
  align-items: center;
  background: linear-gradient(
    135deg,
    var(--backgroundPrimary) 0%,
    var(--backgroundSecondary) 100%
  );
  color: var(--text);
  display: flex;
  /* Change font here */
  font-family:
    -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu,
    sans-serif;
  justify-content: center;
  min-height: 100vh;
  padding: 15px;
}

.profileCard-main {
  background: var(--cardBackground);
  backdrop-filter: blur(10px);
  border-radius: 20px;
  box-shadow: 0 20px 40px rgba(0, 0, 0, 0.5);
  /* Change card width here (make sure to also adjust height
  for profileCard-mainImg class below and check mobile view) */
  max-width: 400px;
  overflow: hidden;
  text-align: center;
  width: 100%;
}

.profileCard-mainImg {
  /* Change main image height here (make sure to also adjust width
  for profileCard-main class above and check mobile view) */
  height: 300px;
  object-fit: cover;
  width: 100%;
}

.profileCard-section {
  padding: 20px;
}

.profileCard-h1 {
  font-size: 2rem;
  font-weight: bold;
  margin-bottom: 10px;
}

.profileCard-bio {
  font-size: 1.1em;
  line-height: 1.5;
  margin-bottom: 30px;
}

.profileCard-mainLink {
  display: block;
}

.profileCard-imgDescription {
  font-size: 0.75rem;
  margin-bottom: 30px;
}

.profileCard-mainLink img {
  border-radius: 15px;
  object-fit: cover;
  transition:
    transform 0.3s ease-in-out,
    box-shadow 0.3s ease-in-out;
  width: 100%;
}

.profileCard-mainLink img:hover {
  box-shadow: 0 10px 25px rgba(0, 0, 0, 0.25);
  transform: translateY(-5px);
}

.profileCard-socialLink {
  align-items: center;
  background: var(--socialLinkBackground);
  border: 2px solid transparent;
  border-radius: 50px;
  color: var(--text);
  display: flex;
  font-size: 1.1em;
  font-weight: 500;
  gap: 15px;
  justify-content: center;
  margin-bottom: 20px;
  padding: 15px 25px;
  transition: all 0.3s ease-in-out;
  text-decoration: none;
}

.profileCard-socialLink:hover {
  background: var(--socialLinkHoverBackground);
  border-color: var(--socialLinkHover);
  box-shadow: 0 10px 25px rgba(0, 123, 255, 0.25);
  color: var(--socialLinkHover);
  transform: translateY(-2px);
}

.profileCard-btn {
  align-items: center;
  background: var(--socialLinkBackground);
  border: 2px solid var(--btnBorder);
  border-radius: 50px;
  color: var(--text);
  cursor: pointer;
  display: flex;
  font-size: 1em;
  font-weight: 500;
  gap: 15px;
  justify-content: center;
  padding: 12px 20px;
  transition: all 0.3s ease-in-out;
  width: 100%;
}

.profileCard-btn:hover {
  background: var(--btnHoverBackground);
}

.profileCard-btn.copied {
  background: var(--btnCopiedBackground);
  color: var(--btnCopied);
}

JavaScript

function copyLink() {
  const copyLinkBtn = document.querySelector(".profileCard-btn");
  const currentUrl = window.location.href;
  const originalBtnContent = copyLinkBtn.innerHTML;

  function updateBtnText() {
    copyLinkBtn.textContent = "Copied!";
    copyLinkBtn.classList.add("copied");

    setTimeout(() => {
      copyLinkBtn.innerHTML = originalBtnContent;
      copyLinkBtn.classList.remove("copied");
    }, 2000);
  }

  if (navigator.clipboard && navigator.clipboard.writeText) {
    navigator.clipboard
      .writeText(currentUrl)
      .then(updateBtnText)
      .catch((err) => console.error("Could not copy page link.", err));
  } else {
    const textArea = document.createElement("textarea");

    textArea.value = currentUrl;
    textArea.style.position = "fixed";
    textArea.style.left = "-9999px";

    document.body.appendChild(textArea);

    textArea.select();

    try {
      document.execCommand("copy");
      updateBtnText();
    } catch (err) {
      console.error("Could not copy page link.", err);
    }

    document.body.removeChild(textArea);
  }
}

You can view the final result in this CodePen.


If you want to see a detailed explanation, you can check this article on my Medium blog.

Thank you for reading.