Blog

Notes and ideas from Moresoda

Imageless CSS iPhone-esque Send Button

I’ve been doing some playing with pseudo-elements recently on a personal project, and ended up doing a little bit of reading up about CSS gradients. I can’t remember where the article was, it’s not important really, but I wanted to share what I’ve managed to achieve with them.

Imageless CSS iPhone-esque Send Button

So my idea was pretty simple, try and recreate an iPhone style “Send” button. My first attempts weren’t too bad, rounded corners were easy, thanks to colorzilla creating gradients was simple, and then I just applied them to a pseudo element, positioned it absolutely and created the overlay. While playing around with the webkit inspector, I noticed when I tried to change the text colour on the button, the gradient was displaying over the top of the text which made it look a bit weird. So I thought that’s simple, I’ll wrap the link text in a span, change the z-index on the span, done. But that’s far too easy, so I thought I’d see if I could do it without any added markup. Behold.

http://dabblet.com/result/gist/1589041

The code breakdown should be quite simple, but I’ll go through it step by step anyway. Because I was playing around with “Tags” I ended up doing this with lists, but it can be done standalone easily enough.

 <ul class="tags">
 	<li>Tagged With</li>
 	<li><a href="#">Tag One</a></li>
 	<li><a href="#">Tag Two</a></li>
 	<li><a href="#">Tag Three</a></li>
 	<li><a href="#">Tag Four</a></li>
 	<li><a href="#">Tag Five</a></li>
 </ul>

 <ul class="tags alt">
 	<li>Tagged With</li>
 	<li><a href="#">Tag One</a></li>
 	<li><a href="#">Tag Two</a></li>
 	<li><a href="#">Tag Three</a></li>
 	<li><a href="#">Tag Four</a></li>
 	<li><a href="#">Tag Five</a></li>
 </ul>

That’s the HTML, two lists. I had mine with a clearfix attached to them, but for this example they don’t have it, hence the clear and margin.

 
    .tags {
    	clear: both;
    	list-style: none;
    	padding: 0;
    	margin: 0 0 50px;
    }

    .tags li {
    	float: left;
    	margin: 0 4px 0 0;
    }

    .tags li:first-child {
    	margin: 4px 4px 0 0;
    }

    .tags li a {
    	display: inline-block;
    	position: relative;
    	font-weight: 700;
    	color: rgba(255,255,255,0.4);
    	padding: 4px 8px;
    	text-decoration: none;
    	border-radius: 12px;
    	-webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.25), inset 0 -1px 1px rgba(0, 0, 0, 0.25); 
    	-moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.25), inset 0 -1px 1px rgba(0, 0, 0, 0.25); 
    	box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.25), inset 0 -1px 1px rgba(0, 0, 0, 0.25); 
    	text-shadow: rgba(0,0,0,0.15) 0 -1px;
    }

    .tags li a:before,
    .tags li a:after {
    	position: absolute;
    	left: 0;
    	display: block;
    	width: 100%;
    	height: 100%;
    	text-indent: -9999px;
    	border-radius: 12px;
     }

    .tags li a:before {
    	content: "Underlay";
    	top: 0;
    	z-index: -10;
    	background: #1a4cde;
    }

    .tags li a:after {
    	content: "Overlay";
    	position: absolute;
    	top: 1px;
    	z-index: -5;

    	background: -webkit-linear-gradient(top, rgba(255, 255, 255, 0.5) 0%,rgba(255, 255, 255, 0) 65%,rgba(255, 255, 255, 0) 66%,rgba(255, 255, 255, 0) 80%,rgba(255, 255, 255, 0.5) 100%);
    	background: -moz-linear-gradient(top, rgba(255, 255, 255, 0.5) 0%,rgba(255, 255, 255, 0) 65%,rgba(255, 255, 255, 0) 66%,rgba(255, 255, 255, 0) 80%,rgba(255, 255, 255, 0.5) 100%);
    	background: linear-gradient(top, rgba(255, 255, 255, 0.5) 0%,rgba(255, 255, 255, 0) 65%,rgba(255, 255, 255, 0) 66%,rgba(255, 255, 255, 0) 80%,rgba(255, 255, 255, 0.5) 100%);
    }

    .tags li a:hover {
    	color: rgba(255,255,255,1);
    }

    .alt li a {
    	color: #fff;
    }

    .alt li a:after {
    	background: -webkit-linear-gradient(top, rgba(255,255,255,0.15) 0%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0) 51%,rgba(255,255,255,0) 100%); 
    	background: -moz-linear-gradient(top, rgba(255,255,255,0.15) 0%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0) 51%,rgba(255,255,255,0) 100%); 
    	background: linear-gradient(top, rgba(255,255,255,0.15) 0%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0) 51%,rgba(255,255,255,0) 100%); 
    }
 

The CSS looks a bit more complicated, but isn’t really. Setting the list styles was the first thing, I’m not going to go into that, I’ll go straight into the nitty gritty of the button itself.

 
  .tags li a {
  	display: inline-block;
  	position: relative;
  	font-weight: 700;
  	color: rgba(255,255,255,0.4);
  	padding: 4px 8px;
  	text-decoration: none;
  	border-radius: 12px;
  	-webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.25), inset 0 -1px 1px rgba(0, 0, 0, 0.25); 
  	-moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.25), inset 0 -1px 1px rgba(0, 0, 0, 0.25); 
  	box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.25), inset 0 -1px 1px rgba(0, 0, 0, 0.25); 
  	text-shadow: rgba(0,0,0,0.15) 0 -1px;
  }

So it’s just an A tag, set as inline block which allows us to change the margin/padding on all four sides. You’ll notice I’ve got an inset shadow, that’s to create the “border” style that the button has. The text shadow simply adds a minor shadow 1 pixel above the text.

  .tags li a:before,
  .tags li a:after {
  	content: "";
  	position: absolute;
  	left: 0;
  	display: block;
  	width: 100%;
  	height: 100%;
  	border-radius: 12px;
   }
 

The before and after elements share a few styles, they’re both absolutely positioned block level elements which span the whole height and width of their parent element. The the border radius is there so the inset box shadow displays nicely.

 
  .tags li a:before {
  	top: 0;
  	z-index: -10;
  	background: #1a4cde;
  }

  .tags li a:after {
  	position: absolute;
  	top: 1px;
  	z-index: -5;
  	background: -webkit-linear-gradient(top, rgba(255, 255, 255, 0.5) 0%,rgba(255, 255, 255, 0) 65%,rgba(255, 255, 255, 0) 66%,rgba(255, 255, 255, 0) 80%,rgba(255, 255, 255, 0.5) 100%);
  	background: -moz-linear-gradient(top, rgba(255, 255, 255, 0.5) 0%,rgba(255, 255, 255, 0) 65%,rgba(255, 255, 255, 0) 66%,rgba(255, 255, 255, 0) 80%,rgba(255, 255, 255, 0.5) 100%);
  	background: linear-gradient(top, rgba(255, 255, 255, 0.5) 0%,rgba(255, 255, 255, 0) 65%,rgba(255, 255, 255, 0) 66%,rgba(255, 255, 255, 0) 80%,rgba(255, 255, 255, 0.5) 100%);
  }
 

The before element is the background colour, so this has a z-index of -10 to pull it all the way back, and that’s about it. The after is a bit more in depth, it’s positioned 1px down from the top so that we get the inset box shadow showing. Then there’s the gradient, which starts at 50% white, which fades to 0% just over half way through the element. The third stop may be superfluous but I’ll leave it in there for now. The fourth brings the gradient back in slightly, and then the fifth just finishes it all off.

  
  .tags li a {
  	...
  	color: rgba(255,255,255,0.4);
  	...
  }

  .tags li a:hover {
  	color: rgba(255,255,255,1);
  }
 

As you can see, the standard state of the element has low opacity text, like an inactive “Send” button, then when you hover over it it comes back up to full opacity, just like the iPhone once you start to type a message.

  .alt li a {
  	color: #fff;
  }

  .alt li a:after {
  	background: -webkit-linear-gradient(top, rgba(255,255,255,0.15) 0%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0) 51%,rgba(255,255,255,0) 100%); 
  	background: -moz-linear-gradient(top, rgba(255,255,255,0.15) 0%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0) 51%,rgba(255,255,255,0) 100%); 
  	background: linear-gradient(top, rgba(255,255,255,0.15) 0%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0) 51%,rgba(255,255,255,0) 100%); 
  }

The alternate style is something I am still playing with, although it was actually my first attempt. It sets a 15% white overlay over the top half of the element, then the bottom half is completely transparent. The third stop here is the important one, it sets the opacity to 0 immediately, which prevents the gradient from fading from 15% to 0% over the last half of the element.

More than anything this has been a trial exercise to see what’s possible with pseudo elements and CSS 3. Feel free to take this content and use it for your own projects, or play around with it. I have only tested it in Chrome 17 currently, so while it will work in the latest version of Firefox, it may need some tweaking to get it spot on.