Skip to content

Daniel Lamb Byte–size Bits & Bobs

Pure CSS Virtual Business Card

13 minute read.

Objective

Handing out printed business cards is so 1999, it’s time for a 21st century upgrade! My goal for this project is to create an interactive virtual business card using only semantic HTML markup and CSS3 (without JavaScript).

Process

Structure

Good web pages start with semantic elements that help define and describe the information. For the font of the card I wanted a photo, name, title and brief bio. For the back, links to my primary social media sites. (CodePen)

<div class="cards">
  <div class="front">
    <img alt="Full Name" src="https://placehold.it/150x150">
    <h1>Full Name</h1>
    <h3>Professional Title</h3>
    <p>Succinct biography that describes relevant experience.</p>
  </div>
  <div class="back">
    <a href="#" title="Follow on Twitter">Twitter</a>
    <a href="#" title="Connect on Linkedin">LinkedIn</a>
    <a href="#" title="Friend on Facebook">Facebook</a>
    ...
    <h5>Find, Follow, &amp; Friend Me Online</h5>
  </div>
</div>

Styling

I have seen tons of examples that use CSS3 to create a “card flip” animation. For this project I wanted something different, something unique, a way to show both side of the card at the same time. So I decided on having one business card displayed like it’s leaning up against a stack them.

I began with some minimal CSS rules to reset the styling of the HTML elements I’m using to a consistent baseline. Rem units were used so I can easily scale things as needed based on a single value. (CodePen)

html, body, div, h1, h3, img, p, a {
  margin: 0;
  padding: 0;
  border: 0;
  font-size: 100%;
  font: inherit;
  vertical-align: baseline;
}
body {
  line-height: 1;
}
html {
  font-size: 16px;
}

Next I aligned the cards to the center of the page and set the font. (CodePen)

.cards {
  position: absolute;
  left: 50%;
  top: 50%;
  margin: -9.44rem 0 0 -15.4rem;
  perspective: 18.88rem;
  width: 30.81rem;
  max-width: 100%;
  height: 18.88rem;
  text-rendering: optimizeLegibility;
  font-family: Trebuchet MS, Lucida Grande, Lucida Sans Unicode, Lucida Sans, Tahoma, sans-serif;
}

Then it was time to make the front card look like a card. Note that the image is sized at 50% so it look sharp on devices with high pixel density1 such as iOS retina displays. (CodePen)

  .front {
    position: absolute;
    z-index: 3;
    bottom: 0;
    right: 0;
    width: 20.19rem;
    height: 12.44rem;
    padding: 0.4375rem 0.4375rem 0 0.4375rem;
    background-color: rgba(234,234,229,1);
    border-left: 0.125rem solid rgba(208,208,208,1);
    border-bottom: 0.125rem solid rgba(180,180,180,1);
    img {
      float: left;
      margin-right: 0.4375rem;
      border-radius: 50%;
      border: solid 0.125rem #fff;
      box-shadow: 0 0 0.0625rem #000;
      width: 4.688rem;
      height: 4.688rem;
      background-color: #7D7D7D;
    }
    h1 {
      font-size: 1.875rem;
      margin: 0 0 0.4375rem 0;
    }
    h3 {
      font-size: 0.875rem;
      font-weight: 700;
      margin: 0;
      color: #515151;
    }
    p {
      clear: left;
      margin: 1.25rem 0 0.75rem 0;
      padding: 0;
      font-size: 0.8125rem;
      color: #313131;
      font-weight: 100;
    }
  }

With the front of the card stying in place, the back of the card got the same treatment using the following CSS rules. (CodePen)

  .back {
    position: absolute;
    z-index: 1;
    top: -0.8rem;
    right: 5rem;
    width: 20.19rem;
    height: 12.44rem;
    padding: 0.3125rem;    
    background-color: rgba(234,234,229,1);
    border-left: 0.125rem solid rgba(208,208,208,1);
    border-bottom: 0.125rem solid rgba(180,180,180,1);
    border-right: 0.0625rem solid rgba(208,208,208,0.5);
    h5 {
      margin: 0.25rem 0.5rem;
      font-weight: 100;
      font-size: 1rem;
    }
  }

Now comes the interesting part, creating a stack of cards! First we need to add a little HTML that we can style to look like a stack of cards.

<div class="cards">
  <!-- ... -->
  <div class="stack"></div>
</div>

For the element I use a repeating linear gradient2 to draw lines that rather nicely simulate a stack of cards at an angle.

  .stack {
    position: absolute;
    z-index: 2;
    height: 3.75rem;
    width: 3.75rem;
    bottom: 7.9em;
    right: 19.9rem;
    background: rgba(234,234,229,1) repeating-linear-gradient(180deg, rgba(234,234,229,1), rgba(0,0,0,0.35) 2%, rgba(0,0,0,.2) 5%);
    transform: rotate(10deg) skew(20deg);
  }

We can further enhance the 3D effect by adding in some shadows to the cards. (CodePen)

  .front {
    // ...
    box-shadow: -5.688rem -1.125rem 6.25rem -2.875rem rgba(0,0,0,0.6);
  }
  .back {
    // ...
    box-shadow: -0.9375rem 3.75rem 3.75rem 0.125rem rgba(0,0,0,0.4);
  }

Social Media

Now that we have a nice looking stack of cards with one on display, I could leave it at that. However, the information on the back of the card is a bit hard to read given its size and angle. So I decided to use SVG social media logos to create a more friendly linking experience.

<div class="cards">
  <!-- ... -->
  <div class="back">
    <a href="#" title="Follow on Twitter" class="twitter">
          <svg><!-- ... --></svg>
        </a>
        <a href="#" title="Connect with on Linkedin" class="linkedin">
          <svg><!-- ... --></svg>
        </a>
        <a href="#" title="Circle on Google+" class="googleplus">
          <svg><!-- ... --></svg>
        </a>
        <a href="#" title="Follow on GitHub" class="github">
          <svg><!-- ... --></svg>
        </a>
        <a href="#" title="Ask a question on Stackoverflow" class="stackoverflow">
          <svg><!-- ... --></svg>
        </a>
        <a href="#" title="Watch on Youtube" class="youtube">
          <svg><!-- ... --></svg>
        </a>
        <a href="#" title="Read books on Leanpub" class="leanpub">
          <svg><!-- ... --></svg>
        </a>
        <a href="#" title="Friend on Facebook" class="facebook">
          <svg><!-- ... --></svg>
        </a>
  </div>
</div>

Just need to add in the markup for the links we want to include and style the SVG logos accordingly. (CodePen)

a {
  display: inline-block;
  border-radius: 50%;
  width: 3.125rem;
  height: 3.125rem;
  text-align: center;
  margin: 0.25rem;
  overflow: hidden;
  background-color: #000;
  svg {
    margin-top: 0.54rem;
    height: 2rem;
    path, polygon {
      fill: #fff;
    }
  }
  &.twitter {
    background-color: #7FAADD;
  }
  &.linkedin {
    background-color: #0077b5;
    svg {
      width: 1.65rem;
    }
  }
  &.googleplus {
    background-color: #d34836;
  }
  &.github {
    // defaults work :)
  }
  &.stackoverflow {
    background-color: #f90;
    svg {
      width: 1.4rem;
    }
  }
  &.youtube {        
    background-color: #b31217;
    svg {
      width: 1.7rem;
    }
  }
  &.leanpub {
    svg {
      width: 2em;
    }
  }
  &.facebook {
    background-color: #3b5998;
  }
}

Problems

I was pleasantly surprised that Internet Explorer 9 wasn’t too bad! The CSS3 animation transitions and transforms gracefully fell back nicely.

Webkit CSS3 Bug

After checking the page in Chrome, Firefox, Opera, Internet Explorer etc. It turned out that only the Webkit browser had a layout issue, caused by an known bug3.

Since Webkit wasn’t honoring the z-index property of the element, I had to get creative. I ended up manually moving the element back behind the front card in 3D space, then scaling it up to it’s original size.

.back {
  // ...
  scale(1.435)
  translate3d(-1rem, -4.063rem, -6.25rem);
}

Interactivity

Now that we have the HTML structure and styling worked out, it’s time for the interactive part. Here the idea is that when someone interacts with the card on the top of the stack it is “picked up” and brought forward into view. To achieve this effect I used CSS3 transitions and :hover pseudo-class.

.back {
  transition: top 0s, transform 0.2s, z-index 0.2s;
  &:hover {
    z-index: 4;
    top: 2.5rem;
    transition: top 0.4s, transform 0.2s, z-index 0.15s;
  }
}

By defining a transition for the both the default and hover states it is possible to have different “intro” and “outro” animations. In this case the card looks like it is picked-up more slowly than it is put back.

Microdata

One final improvement is to define the meaning of the information in the card so machines can understand it as well. For example, when we define <h1>Full Name</h1>, the web browser and search engines only know that this section is an important heading. But they don’t know about the data inside it, that in this case the tag is used to contain a person’s name.

This is where using a standard vocabulary such as from schema.org can help. Because all of the information available is already displayed in the card, I chose the microdata4 format to augment the semantic markup with structured data. A quick way to verify you have things done right is to use the free structured data testing tool created by Google. You can check any public web page or simply paste in the code you are experimenting with to look for errors and warnings.

Result

There you have it! An AMP HTML5 friendly virtual business card that looks great on any device! Using this structure you can quickly change the contents such as your bio and the social media you want to include.

To further illustrate how flexible this responsive setup is, I swapped out Stackoverflow for Snapchat on the back of my card. (CodePen)

  1. Pixel Density is a measurement of the resolution of an electronic image device.

  2. Repeating Linear Gradient is a CSS3 function that can draw repeating lines.

  3. Webkit CSS3 Transform Bug caused a layout issue with the back card.

  4. Microdata is a specification used to structure information in HTML that search engines can then extract.

  5. AMP HTML is a way of creating web pages that consistently render quickly.


Give Feedback via my AMA (ask me anything) project on GitHub. Published on by Daniel Lamb.
You may also enjoy these posts