2

I'm trying to make a button using three background images so that we can pull in translations for the the text of the button and expand nicely. We'll probably add a base style for IE8 but our designer wants us to use this style and we couldn't recreate it nicely with pure CSS3.

Here are the images:
Button front end Tiny middle piece to repeat Button rounded end piece

Here's the HTML (just a simple button, but thought I should put it anyway:

<button class="back clickable" aria-label="Back" onclick="javascript:history.back();">Back</button>

I've already tried a couple of things; I'll paste the CSS of both attempts.

Attempt 1: Using Pseudo-selectors
http://jsfiddle.net/c2B6X/

.back {
    background: url("images/back-middle.png") 14px 0 repeat-x;
    color: $white;
    height: 28px;
    padding: 5px;
    &:before {
        background: url("images/back-front.png") 0 0 no-repeat;
        width: 14px;
    }
    &:after {
        background: url("images/back-end.png") 100% 0 no-repeat;
        width: 8px;
    }
}

Attempt 2: Three background-images
http://jsfiddle.net/nPUQN/

.back {
    background: none;
    background-image: url("images/back-middle.png"), url("images/back-end.png"), url("images/back-front.png");
    background-position: 14px 0, 100% 0, 0 0;
    background-repeat: repeat-x, no-repeat, no-repeat;
    border-right: 8px transparent;
    border-left: 14px transparent;
    color: $white;
    height: 28px;
    padding: 5px;

}

If it looks like atypical CSS that's because we're using SASS.

Is there something obvious I'm missing or doing wrong? Any advice on how to make this work would be greatly appreciated.

EDIT
Since I got so many answers that "work", I'll mark correct the answer that works best in Chrome, FF and IE9.

EDIT 2
I've tried all answers and none work in IE9. We have to support IE9 (and IE8, but I won't even go there for now). I'm going to start a bounty. Anyone who can supply an answer that works for IE9, Firefox and Chrome gets it.

elemjay19
  • 1,126
  • 3
  • 25
  • 51

6 Answers6

7

Pseudo-content requires content, so you'll first need to specify that:

.selector::before {
  content: ' ';
}

Then to define any layout such as width and height you'll need to display the pseudo elements as a block or inline-block. Block layout will force each pseudo element to wrap and inline-block will sit on the line so you'll either have to use floats or absolute positioning.

.selector {
  position: relative;
  height: 28px;

  /* allow for the pseudo-elements which do not have layout due to absolute positioning */
  margin: 0 15px;
}
.selector::before,
.selector::after {
  content: ' ';
  position: absolute;
  top: 0;
  width: 15px;
  height: 28px;
}
.selector::before {
  left: -15px;
}
.selector::after {
  right: -15px;
}

Demo here for you: http://codepen.io/anon/pen/yaJGI

i_like_robots
  • 2,760
  • 2
  • 19
  • 23
  • 1
    i have forked your pen with real image to make it more efficient as 'how to' : http://codepen.io/gcyrillus/pen/aKDkw ;) +1 for content:''; – G-Cyrillus Jul 13 '13 at 00:31
  • 2
    This will work in IE9 if you set the overflow to visible on the button: http://codepen.io/cimmanon/pen/xKpaB or http://codepen.io/cimmanon/pen/rDpFn – cimmanon Jul 15 '13 at 18:36
  • I feel like I should split the bounty between i_like_robots, @cimmanon and [GCyrillus](http://stackoverflow.com/users/2442099/gcyrillus) because it's the three of you that put together the answer I needed. – elemjay19 Jul 15 '13 at 20:59
  • 1
    i_like_robots did most of the work. We knew what we were getting into when we contributed to an existing answer rather than providing our own. – cimmanon Jul 15 '13 at 21:07
  • Fair enough. Accepting answer. Will assign bounty when I'm allowed. – elemjay19 Jul 15 '13 at 21:24
  • 1
    what matters is that you get your answer, and we all enjoyed it ;) – G-Cyrillus Jul 15 '13 at 21:43
2

You'll need to add content for :before and :after to show. After that, you can position them absolutely and by giving them right: 100% and left: 100% respectively, you can position them in front of and behind the button.

button {
  background:transparent;
  border: none;
  padding: 0;
  margin: 0;
  line-height: 1;
  font-size: 12px;
  cursor: pointer;
  margin-left: 14px; /* width of :before */
}
.back {
    background: url("https://i.stack.imgur.com/DaQcG.png") 14px 0 repeat-x;
    color: white;
    height: 28px;
    padding: 5px;
    position: relative;
}
.back:before {
        position: absolute;
        content: "";
        height: 28px;
        top: 0;
        right: 100%;
        background: url("https://i.stack.imgur.com/6m2HC.png") 0 0 no-repeat;
        width: 14px;
    }
.back:after {
        position: absolute;
        content: "";
        height: 28px;
        top: 0;
        left: 100%;
        background: url("https://i.stack.imgur.com/2WA5B.png") 100% 0 no-repeat;
        width: 8px;
    }

The definitions of before and after are slightly the same, so you could write it down more compactly, but you need to re-sass it anyway. ;)

http://jsfiddle.net/c2B6X/

Tip: Note that downloading three images is less efficient. You can create one image that contains the start and end at the top, and the middle part at the bottom. By positioning the background, you can show the right part inside the elements. This technique is called sprites and it decreases the number of requests to make.

GolezTrol
  • 114,394
  • 18
  • 182
  • 210
1

I came up with a little something that you can take a look at. You can modify it to best fit your needs.

http://jsfiddle.net/Xy7Hv/1/

HTML:

<button class="back">Back</button>

CSS:

.back {
    border: none;
    height: 28px;
    padding-right: 8px;
    padding-left: 14px;
    background-image: url("https://i.stack.imgur.com/DaQcG.png"),             
        url("https://i.stack.imgur.com/6m2HC.png"), 
        url("https://i.stack.imgur.com/2WA5B.png");
    background-position: 14px 0px, left, right;
    background-size: 30px 100%, 14px 28px, 8px 28px;
    background-repeat:  no-repeat,no-repeat,no-repeat;
}

("background-size: 30px" is the width of the button, so if all your buttons are the same size it shouldn't be a problem)

Stephen
  • 576
  • 8
  • 19
  • 1
    good trick but not enough flexible in my humble opinion, if font-size, letter-spacing is increased or other font used, you may have a white spot ;) – G-Cyrillus Jul 13 '13 at 00:13
  • So what your saying is you don't plan on it being the same size every time. – Stephen Jul 13 '13 at 00:21
  • i do not plan anything, it is just the kind of things that happens all the time :), user have their own browsers and setup and the way fonts are handled is the most difficult thing to control. – G-Cyrillus Jul 13 '13 at 00:24
  • And I did say that it will change size depending on the length of the localized string. It doesn't work well with different sizes. I tried bolding it and that didn't even work. – elemjay19 Jul 13 '13 at 00:26
  • Ohh ok, I understand. That's about all I know how to do as of right now. Well, without the use of javascript anyway. – Stephen Jul 13 '13 at 00:28
1

with your multiple background version, you could add gradient or white image to build your button bg , keeping some space with padding. http://jsfiddle.net/nPUQN/1/

.back {
    background:
        url("https://i.stack.imgur.com/2WA5B.png") 100% 0 no-repeat ,
        url("https://i.stack.imgur.com/6m2HC.png") 0 0 no-repeat,
       -webkit-linear-gradient(0deg, white 0, white 14px , transparent 14px ,transparent) 0 0 no-repeat ,
        -webkit-linear-gradient(180deg, white 0, white 8px , transparent 8px ,transparent) 0 0 no-repeat ,
        url("https://i.stack.imgur.com/DaQcG.png") 14px 0 repeat 
        ;
    color: $white;
    height: 28px;
    padding: 5px 8px 5px 14px;
}

prefixed for chrome, add other prefix needed or use a prefix js :)

G-Cyrillus
  • 101,410
  • 14
  • 105
  • 129
1

I add this answer because i like to keep the other as it is. This one is to be tested in IE8/9 with pseudo and position: http://codepen.io/gcyrillus/full/lBpaI or to edit : http://codepen.io/gcyrillus/pen/lBpaI

.back {
    background:
        url("https://i.stack.imgur.com/DaQcG.png") 14px 0 repeat 
        ;
    color: white;
    height: 28px;
    padding: 5px;
  position:relative;
  overflow:visible;
}
.back:before {
  content:url(https://i.stack.imgur.com/6m2HC.png);
  top:0;
  left:-14px;
  position:absolute;
}
.back:after {
  content:url(https://i.stack.imgur.com/2WA5B.png);
  position:absolute;
  right:-8px;
  top:0;
}
G-Cyrillus
  • 101,410
  • 14
  • 105
  • 129
  • 1
    this is similar answer of I_like_robots, just cleaned up ! (was offline and missed some comments and fixed.) So see other answer before giving me points here :). – G-Cyrillus Jul 15 '13 at 21:47
0

I used this code today. It's similar to your 2nd example, but uses the background shortcut property and a mixture of position strings.

background: url("../images/img01.png") 0px 0px no-repeat, url("../images/img02.png") 53px 0px repeat-x, url("../images/img03.png") right top no-repeat;

img01 = left image (53px wide)

img02 = fill image

img03 = right image

L_Holcombe
  • 387
  • 2
  • 8