Tutorial — Creating a Custom Tweet Button with a Counter

January 19th, 2012 :: Web Development

custom tweet buttons

Tweet buttons are a great way to spread your content to the public. Twitter provides an easy way to include their button on your site, but this comes at the cost of not having a very customizable style.

The button is served inside an iframe, which restricts CSS and Javascript access. This can make it hard to get the button to fit in with the rest of the design. What’s even worse is that while the button wrapper is set at a fixed width, the counter inside isn’t. Once the page starts getting more tweets and the counter increases, the width of the button expands, but since the parent iframe has a fixed width, it’s impossible for us to know the actual width of the button.

Something that can also be seen as an issue is the size (46.5 kB) of the widget.js script that Twitter injects with the default button. In most cases though, this script file should already be in the visitor’s cache.

Building the Button

At its core, building a tweet button is as easy as creating a link to Twitter’s tweet intent, with some added URL parameters to set the tweet text. But you probably also want to display the tweet count of the page, to brag about how popular and awesome your site is. This requires us to use Twitter’s count API.

The twitter bird

The count API is currently a “private” API, which in other words mean that it’s not linked to in any of Twitter’s developer documentation. This doesn’t mean we can’t access it though, and by looking through the widget.js script, the URL is easily found.

The reason Twitter labels the API as private is because they’re concerned about scaling, but if we were to use the default button, we would still be using this same exact API. In this case, we aren’t adding any extra load to Twitter’s servers, we are actually using less of their bandwidth, since we aren’t downloading the widget.js file with each visit.

Alright, let’s get to the code!


<a href="#" class="tweet" data-via="JoelBesada" data-hashtags="cool">        
       <span class="count">0</span>
       <span class="message">Tweet</span>  

In the HTML, we can set different options for the tweet with the data attributes, using the same syntax as the Twitter button builder. The href is left empty, as we are going to parse the data attributes and create the tweet intent link with Javascript.

In my example, I want the tweet count to display first, and the text “Tweet” when the button is hovered over, so I have two span elements for this.


The vendor prefixes below have been omitted to keep the CSS clean.

.tweet {
    position: relative;
    display: inline-block;
    padding: 7px 10px;
    width: auto;
    min-width: 50px;

    text-align: center;
    font: 16px Helvetica, Arial, sans-serif;
    color: white;
    text-shadow: 1px 1px 0 #021D2B;
    text-decoration: none;

    background: blue;
    background-color: #209adb;
    background-image: repeating-linear-gradient(-45deg, transparent, transparent 5px, rgba(0,0,0,.1) 5px, rgba(0,0,0,.1) 10px),
                      linear-gradient(top, #5ab9ed 0%,#209adb 100%);

    border: 1px solid #04141C;
    border-bottom-width: 3px;
    box-shadow: 0 2px 3px rgba(0,0,0,0.1), 
                inset 0 1px 0 rgba(255,255,255,0.4), 
                inset 0 0 3px rgba(255,255,255,0.5);


/* Twitter icon */
.tweet:before {
    content: "";
    display: block;
    position: absolute;
    left: -35px;
    top: 7px;
    width: 24px;
    height: 24px;

    /* Base64 PNG icon shortened down to prevent chaos */
    pointer-events: none;

/* Extra shadow */
.tweet:after {
    content: "";
    display: block;
    position: absolute;
    z-index: -1;
    left: 0;
    bottom: 20px;
    width: 100%;
    height: 20px;
    box-shadow: 0 26px 10px rgba(0,0,0,0.35);

.tweet .count {
    position: relative;
    z-index: 2;
    transition: opacity 0.25s ease-out;

.tweet .message {
    display: block;
    position: absolute;
    padding-top: inherit;
    top: 0;
    left: 0;

    width: 100%;
    height: 100%;
    opacity: 0;
    text-align: center;
    transition: opacity 0.25s ease-out;

.tweet:hover {
    animation: bgpos 0.3s infinite linear;

.tweet:hover .count {
    opacity: 0;    

.tweet:hover .message {
    opacity: 1;

.tweet:active {
    border-bottom: 1px solid #04141C;
    top: 2px;

    box-shadow: inset 0 2px 3px rgba(0,0,0,0.7), 
                0 1px 0 rgba(255,255,255,0.2), 
                inset 0 0 3px rgba(0,0,0,0.5);

    background-image: repeating-linear-gradient(-45deg, transparent, transparent 5px, rgba(0,0,0,.1) 5px, rgba(0,0,0,.1) 10px),
                      linear-gradient(top, #106491 0%, #106491 100%);

.tweet:active:after {
    display: none;

.tweet:active:before {
    top: 5px;

@keyframes bgpos {
    from { background-position: 0 0; }
    to { background-position: 14px 0; }

I might have gone a little bit overboard with the CSS on this one, but I’ll explain some of the trickier parts before we get on to the Javascript.

The background is created with multiple gradients, which are comma-separated and ordered so that the topmost layer is defined first. The striped pattern is created with a repeating linear gradient, to see more of these I recommend you check out this awesome gallery with different CSS3 patterns by Lea Verou. Below the stripes, we have a simple linear gradient between two shades of light blue.

To create some depth in the button, I’m using several box shadows. The shiny and sharp look is achieved by using inset bright shadows, one on the top and another one around the whole border. By increasing the size of the bottom border, we get a sense of height. This effect is also amplified by adding another thicker shadow below the button with the help of the :after pseudo element.

Something that can’t be seen from the picture, but is apparent in the demo, is the looping animation that displays when hovering over the button. By animating the background position, the stripes move across the button and we get a cool looking hover effect. For doing this, we use a simple @keyframe definition with start and end background-position values, calibrating the end x-value to make sure that the animation looks seamless when looped.

For the active state (button pressed down), we darken the background colors, and set some dark inset shadows. We also replace the extra bottom border width and the drop-shadow with a one pixel highlight.

The Javascript (using jQuery)

var API_URL = "http://cdn.api.twitter.com/1/urls/count.json",
    TWEET_URL = "https://twitter.com/intent/tweet";
$(".tweet").each(function() {
    var elem = $(this),
    // Use current page URL as default link
    url = encodeURIComponent(elem.attr("data-url") || document.location.href),
    // Use page title as default tweet message
    text = elem.attr("data-text") || document.title,
    via = elem.attr("data-via") || "",
    related = encodeURIComponent(elem.attr("data-related")) || "",
    hashtags = encodeURIComponent(elem.attr("data-hashtags")) || "";
    // Set href to tweet page
        href: TWEET_URL + "?hashtags=" + hashtags + "&original_referer=" +
                encodeURIComponent(document.location.href) + "&related=" + related +
                "&source=tweetbutton&text=" + text + "&url=" + url + "&via=" + via,
        target: "_blank"
    // Get count and set it as the inner HTML of .count
    $.getJSON(API_URL + "?callback=?&url=" + url, function(data) {

I’ve written the code to be reusable so that anyone can easily copy and paste it into their own sites. The code looks for elements with the class tweet and uses its data attributes to create a tweet intent link with the correct URL parameters. If not set, the URL and text parameters default to the URL and title of the current page, respectively. You can use the the Twitter button builder to generate the different data attributes for your button.

The count is retrieved by sending a GET request to the Twitter count API, with a parameter set to the URL we want to get the tweet count on. AJAX requests are usually restricted by the same origin policy, which means we can’t make requests to services on other domains. There are two solutions to this, CORS (Cross-Origin Resource Sharing) and JSONP (JSON with padding).

The API we’re accessing is not CORS enabled, but it does have support for JSONP. What this means is that when we set the callback parameter to for example callMe, the API will set the return value as an argument to the callback function, like this.

The jQuery getJSON function handles this automatically when given an url with the callback parameter set to “?”. The second argument of the getJSON function gets called when the data is returned, and from there we can easily set the HTML of the .count element to the returned value.


That’s basically all there is to it, I hope you find some value in what I’ve covered here. I’ve created a demo on JSFiddle for everyone to check out, go ahead and click the button below!


  • Nowprojector

    Now, can you tell us how to make a custom Like button, like your beige/gray/orange one?

  • http://twitter.com/snaphuman Fabian Hernandez

    Many thanks, this is a very useful resource. salute

  • André

    How to open in a popup?

    • Chris

      For those who are looking to open it on a new window add something like the code below inside the ‘a’ tag, change the link to your own by right clicking your tweet button and copying the link location.


      return false;”

  • nickofnz

    That’s a beautiful thing  – have you got the same for a facebook like button?

  • Raulvincente

    Thanks I was looking for this button for ages!!

  • http://www.facebook.com/marie.kim.165 Marie Kim

    Great tutorial! Exactly what I have been looking for!

  • Ludacris

    Awesome, thanks!

  • Chris

    Awesome! Just what I have been looking for, thank you!

  • Yurop

    don’t work! Help me!