Twitter profile card

@ July 31, 2012 8 , , , ,

It's really fun to play with API of Twitter, it gives quite a lot of possibilities, though it have request limit equal 150/hour. Today's project is a profile card based on API of Twitter. There're two themes: dark and light, you can also specify if you like to add arrow to that speech bubble or not. The profile of user which you want is specified by data-username="". Check HTML and JS to learn more.

5 Back

HTML

<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>Twitter profile card - Design it & Code it</title>
  <meta name="viewport" content="width=device-width">
  <link rel="stylesheet" href="reset.css">
  <link rel="stylesheet" href="style.css">
</head>
<body>

  <div class="twitter-card dark bubble preload" data-username="Idered"></div>
  <br>
  <!-- <div class="twitter-card light preload" data-username="Idered"></div> -->

  <script src="js/jquery.js" type="text/javascript"></script>
  <script src="js/script.js" type="text/javascript"></script>
</body>
</html>

CSS

html { background: #f5f5f5 url(img/bg.png); }
body {
  margin: 70px auto 0 auto;
  max-width: 450px;
  width: 100%;
}

@font-face {
    font-family: 'ColaborateThinRegular';
    src: url('fonts/ColabThi-webfont.eot');
    src: url('fonts/ColabThi-webfont.eot?#iefix') format('embedded-opentype'),
         url('fonts/ColabThi-webfont.woff') format('woff'),
         url('fonts/ColabThi-webfont.ttf') format('truetype'),
         url('fonts/ColabThi-webfont.svg#ColaborateThinRegular') format('svg');
    font-weight: normal;
    font-style: normal;
}
@font-face {
    font-family: 'ColaborateRegular';
    src: url('fonts/ColabReg-webfont.eot');
    src: url('fonts/ColabReg-webfont.eot?#iefix') format('embedded-opentype'),
         url('fonts/ColabReg-webfont.woff') format('woff'),
         url('fonts/ColabReg-webfont.ttf') format('truetype'),
         url('fonts/ColabReg-webfont.svg#ColaborateRegular') format('svg');
    font-weight: normal;
    font-style: normal;
}

/*
 * Dark theme
 */
.twitter-card.dark {
  box-shadow: 0 1px 0 rgba(255, 255, 255, .2) inset;
  background-color: #222;
  border-color: #000;
  color: #a0a0a0;
}
.twitter-card.dark .photo { border-color: #000; }
.twitter-card.dark .name,
.twitter-card.dark a { color: #a0a0a0; }
.twitter-card.dark a:hover { color: #2e7ef6; }
.twitter-card.dark .status {
  box-shadow: 0 1px 0 rgba(255, 255, 255, .2) inset;
  text-shadow: 0 1px 0 rgba(0, 0, 0, .5);
  background-color: #3b3b3b;
  border-color: #000;
  color: #ccc;
}
.twitter-card.dark .status:after { border-color: transparent transparent transparent #3b3b3b; }
.twitter-card.dark .status:before { border-color: transparent transparent transparent #000; }
.twitter-card.dark .follow-btn { border-top: 1px solid #000; color: #fff; }
.twitter-card.dark .follow-btn:hover { background-color: #4d91f6; color: #fff; }
.twitter-card.dark .follow-btn:active {
  -webkit-box-shadow:0 1px 0 rgba(255, 255, 255, .4), 0 0 20px rgba(0, 0, 0, .5) inset;
  -moz-box-shadow:0 1px 0 rgba(255, 255, 255, .4), 0 0 20px rgba(0, 0, 0, .5) inset;
  box-shadow:0 1px 0 rgba(255, 255, 255, .4), 0 0 20px rgba(0, 0, 0, .5) inset;
}
/*
 * White theme
 */
.twitter-card.light {
  -webkit-box-shadow: 0 1px 0 rgba(255, 255, 255, 1) inset;
  -moz-box-shadow: 0 1px 0 rgba(255, 255, 255, 1) inset;
  box-shadow: 0 1px 0 rgba(255, 255, 255, 1) inset;
  background-color: #fff;
  border-color: #bbb;
  color: #999;
}
.twitter-card.light .photo { border-color: #000; }
.twitter-card.light .name,
.twitter-card.light a { color: #a0a0a0; }
.twitter-card.light a:hover { color: #2e7ef6; }
.twitter-card.light .status {
  -webkit-box-shadow: 0 1px 0 rgba(255, 255, 255, 1) inset;
  -moz-box-shadow: 0 1px 0 rgba(255, 255, 255, 1) inset;
  box-shadow: 0 1px 0 rgba(255, 255, 255, 1) inset;
  text-shadow: 0 1px 0 rgba(255, 255, 255, 1);
  background-color: #e2e2e2;
  border-color: #c3c3c3;
  color: #999;
}
.twitter-card.light .status:after { border-color: transparent transparent transparent #e2e2e2;}
.twitter-card.light .status:before { border-color: transparent transparent transparent #c3c3c3;}
.twitter-card.light .follow-btn { background-color: #68ace5; color: #fff; }
.twitter-card.light .follow-btn:hover { background-color: #6fb8f5; color: #fff; }
.twitter-card.light .follow-btn:active {
  -webkit-box-shadow: 0 0 20px rgba(0, 0, 0, .2) inset;
  -moz-box-shadow: 0 0 20px rgba(0, 0, 0, .2) inset;
  box-shadow: 0 0 20px rgba(0, 0, 0, .2) inset;
}
/*
 * Basic style
 */
.twitter-card {
  padding: 20px;
  border-width: 1px;
  border-style: solid;
  -webkit-border-radius: 5px;
  -moz-border-radius: 5px;
  border-radius: 5px;
  font-family: 'ColaborateRegular';
  max-width: 400px;
  overflow: hidden;
}
.twitter-card a { text-decoration: none; }
.twitter-card .header { padding-left: 79px; }
.twitter-card .header:after { content: ''; display: table; clear: both; }
.twitter-card .info { float: left; }
.twitter-card .photo {
  display: block;
  float: left;
  border-style: solid;
  position: relative;
  border-width: 1px;
  height: 64px;
  width: 64px;
  margin: 0 15px 0 -79px;
}
.twitter-card .photo,
.twitter-card .photo img,
.twitter-card .photo:after {
  -webkit-border-radius: 5px;
  -moz-border-radius: 5px;
  border-radius: 5px;
}
.twitter-card .photo:after {
  -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .4);
  -moz-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .4);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, .4);
  position: absolute;
  content: '';
  left: 0;
  top: 0;
  z-index: 10;
  width: 100%;
  height: 100%;
}
.twitter-card .photo img {
  height: 100%;
  width: 100%;
}
.twitter-card .name {
  font-family: 'ColaborateThinRegular';
  font-size: 24px;
}
.twitter-card .name,
.twitter-card .username { display: inline-block; }
.twitter-card .username { font-size: 12px; }
.twitter-card .desc {
  padding: 0;
  font-size: 12px;
  margin: 0
}
.twitter-card .status {
  clear: both;
  font-size: 12px;
  border-width: 1px;
  border-style: solid;
  -webkit-border-radius: 5px;
  -moz-border-radius: 5px;
  border-radius: 5px;
  margin-top: 20px;
  padding: 10px 13px;
  position: relative;
}
.twitter-card.bubble .status:after,
.twitter-card.bubble .status:before {
  content: '';
  position: absolute;
  left: 30px;
  line-height: 18px;
  bottom: 100%;
  width: 0px;
  height: 0px;
  border-style: solid;
}
.twitter-card.bubble .status:after {
  margin-bottom: -1px;
  border-width: 14px 0 0 14px;
}
.twitter-card.bubble .status:before {
  left: 29px;
  border-width: 15px 0 0 15px;
}
.twitter-card .stats {
  padding: 15px 0;
  text-align: center;
}
.twitter-card .stats span {
  padding: 0 10px;
  font-size: 14px;
}
.twitter-card .follow-btn {
  -webkit-box-shadow: inset 0 1px 0 rgba(255,255,255,.2);
  -moz-box-shadow: inset 0 1px 0 rgba(255,255,255,.2);
  box-shadow: inset 0 1px 0 rgba(255,255,255,.2);
  background-color: #2e7ef6;
  display: block;
  color: #fff;
  font-size: 22px;
  text-align: center;
  margin: 0 -20px -20px;
  padding: 15px 0;
  text-shadow: 0 1px 2px rgba(0,0,0,.5);
}
.twitter-card.preload * { display: none; }
.twitter-card.preload {
  background-repeat: no-repeat;
  background-position: center center;
}
.twitter-card.dark.preload { background-image: url(img/loader-dark.gif); }
.twitter-card.light.preload { background-image: url(img/loader-light.gif); }

JavaScript

/* Twitter profile card by @Idered | http://designitcodeit.com/i/12 */
;(function ( $, window, document, undefined ) {

  function tmpl(s,d){
    for(var p in d) s=s.replace(new RegExp('{'+p+'}','g'), d[p]);
    return s;
  }

  function parseLinks(text) {
    var patterns = {
      link: /(\b(https?|ftp|file):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])/ig,
      user: /(^|\s)@(\w+)/g,
      hash: /(^|\s)#(\w+)/g
    };
    return text
      .replace(patterns.link,'<a href="$1" target="_blank">$1</a>')
      .replace(patterns.user, '$1@<a href="http://www.twitter.com/$2" target="_blank">$2</a>')
      .replace(patterns.hash, '$1#<a href="http://search.twitter.com/search?q=%23$2" target="_blank">$2</a>');
  }

  $('.twitter-card').each(function () {
    var $card = $(this),
      username = $card.data('username');

    $.ajax({
      type: 'GET',
      url: 'https://api.twitter.com/1/users/show.json',
      timeout: 5000,
      data: {screen_name: username},
      dataType: 'jsonp',
      success: function(data) {
        var user = {
              url: data.url,
              name: data.name,
              username: data.screen_name,
              description: parseLinks(data.description),
              followers: data.followers_count,
              following: data.friends_count,
              tweets: data.statuses_count,
              status: parseLinks(data.status.text),
              photo: data.profile_image_url.replace('normal', 'reasonably_small')
            },
            $template = $(
              tmpl(''.concat(
                '<div class="twitter-card-inner">',
                  '<div class="header">',
                    '<a href="{url}" class="photo" target="_blank">',
                      '<img src="{photo}" alt="{name}">',
                    '</a>',
                    '<a href="{url}" class="name" target="_blank">{name}</a>',
                    '<a href="http://twitter.com/{username}" class="username" target="_blank">@{username}</a>',
                    '<p class="desc">{description}</p>',
                  '</div>',
                  '<div class="status">{status}</div>',
                  '<div class="stats">',
                    '<span>Tweets: {tweets}</span>',
                    '<span>Followers: {followers}</span>',
                    '<span>Following: {following}</span>',
                  '</div>',
                  '<a href="http://twitter.com/intent/follow?screen_name={username}" class="follow-btn" target="_blank" title="Follow me on Twitter">Follow me</a>',
                '</div>'), user));

        $card.append($template)
          .children().hide()
          .parent().removeClass('preload')
          .children().animate({height: 'toggle'}, 'fast');

      }, error: function() {
        $card.text('An error occurred. Probably this user doesn\'t exist.').removeClass('preload');
      }
    });

  });

})( jQuery, window, document );