CSSで背景画像の配置を右から指定する方法

今仕事でスマホ用のサイトを作っているのだけど、ページの先頭へ戻るボタンをCSSで実装したかった。ので、実装した。

こんな感じの。
ページの先頭へ戻るボタンのサンプル

実際はbox-shadowとかtext-shadowとか細かい指定をしているのだけど、今回の例では割愛。ちなみにHTMLは以下。

<p class="hoge">
    <span>hogera</span>
    <a href="#top">TOP</a>
</p>

単純に考えると、a要素にboder指定してグラデーションかけて、矢印の背景とか位置とかを指定して、適当にpadding付ければOKなはず。

.hoge a {
    background-image: url("arrow.png"), -webkit-gradient(linear, left top, left bottom, from(#FFF), to(#999));
    background-image: url("arrow.png"), -webkit-linear-gradient(top, #FFF, #999);
    background-image: url("arrow.png"),    -moz-linear-gradient(top, #FFF, #999);
    background-image: url("arrow.png"),         linear-gradient(to bottom, #FFF, #999);
    background-position: 40px 50%, 0 0;
    background-repeat: no-repeat;
    border: 1px solid #999;
    border-radius: 3px;
    color: #000;
    display: inline-block;
    font: 16px/24px "Helvetica", "Arial", sans-serif;
    padding: 0 17px 0 6px;
    text-decoration: none;
}

View Demo: Right Start Background Position – Demo1

これで想定通りの見た目になる。現時点での最新であるFirefox 18.0.2とChrome 24.0.1312.57 mでは。IEは知らない。で、スマホを想定するならこれでOKなのだけど、いかんせん機種依存という厄介な代物があって、機種によってフォントが違っていたり、文字間隔が違っていたりするので(と言うか、環境によって表示や挙動に違いが出るのはスマホもPCも同じだった)、背景画像を左から指定していると、テキストと重なってしまう可能性がある。というか実際重なった。

今回の場合、左右のpaddingを6px、矢印の画像とテキストの間を2px取りたい。で、今回矢印の画像の横幅は9pxなので、右側のpaddingは9px + 6px + 2px = 17pxとなる。あとは矢印の画像の位置を縦に50%でセンタリングし、横は右から6pxの位置に指定すればOK。なのだけど、現状背景画像は右から指定できない。

背景画像、右から指定させてよー>< w3.org/TR/css3-backgr…

Twitter / yoruaki: 背景画像、右から指定させてよー>< http://t …

ってな具合に、思わずつぶやいちゃうレベル。

で、ここからが本題。背景画像の配置を右から指定する方法。

.hoge a {
    background-image: -webkit-gradient(linear, left top, left bottom, from(#FFF), to(#999));
    background-image: -webkit-linear-gradient(top, #FFF, #999);
    background-image:    -moz-linear-gradient(top, #FFF, #999);
    background-image:         linear-gradient(to bottom, #FFF, #999);
    border: 1px solid #999;
    border-radius: 3px;
    color: #000;
    display: inline-block;
    font: 16px/24px "Helvetica", "Arial", sans-serif;
    padding: 0 17px 0 6px;
    position: relative;
    text-decoration: none;
}

.hoge a:after {
    background: transparent url("arrow.png") no-repeat 0 0;
    content: "";
    height: 12px;
    position: absolute;
    right: 6px;
    top: -webkit-calc(50% - (12px / 2));
    top:    -moz-calc(50% - (12px / 2));
    top:         calc(50% - (12px / 2));
    width: 9px;
}

View Demo: Right Start Background Position – Demo2

a要素にposition:relative;で予め基準点を明示しておき、after擬似要素で矢印の画像をposition:absolute;で右から絶対配置してやるというやり方。縦に関しては、50%で真ん中に指定すると、画像の上部が50%の部分に配置されてしまうので、画像の上部を50%の位置に配置して、その画像の縦半分のサイズを上にずらしている。

ただ、今回は割り切れるから良いのだけど、小数点以下のピクセル値の扱いは各ブラウザによって違うようなので、注意が必要かも。

@yoruaki border-right: 10px transparent; padding-right:20px; background-position:right center;とか

Twitter / forty4_jp

背景画像を右配置にしておき、透明なborderで幅を取るというアドバイスを頂いたので、実験してみた。コードは以下。

hoge a {
    background-image: url("arrow.png"), -webkit-gradient(linear, left top, left bottom, from(#FFF), to(#999));
    background-image: url("arrow.png"), -webkit-linear-gradient(top, #FFF, #999);
    background-image: url("arrow.png"),    -moz-linear-gradient(top, #FFF, #999);
    background-image: url("arrow.png"),         linear-gradient(to bottom, #FFF, #999);
    background-position: 100% 50%, 0 0;
    background-repeat: no-repeat;
    border: 1px solid #999;
    border-right: 6px transparent;
    border-radius: 3px;
    color: #000;
    display: inline-block;
    font: 16px/24px "Helvetica", "Arial", sans-serif;
    padding: 0 11px 0 6px;
    text-decoration: none;
}

View Demo: Right Start Background Position – Demo3

今回の仕様上、すでにborderが指定されているので、右のボーダーを透明にすると、ボタンの右ボーダーが透明になってしまうので今回の仕様にはそぐわなかった。けど、borderを指定しないボタンなどなどには有効な記述方法だと思う。アドバイスありがとうございました。

上記のコード3つを比較したものが以下。多分スマホで見ると、それぞれ表示が異なってくると思う。Demo2が今回の仕様に沿った実装方法。

View Demo: Right Start Background Position – Comparison

追記

早速の反応ありがとうございます!

というわけで、もっとスマートで良い記述を教えてもらったので紹介。

hoge a {
    background-image: url("arrow.png"), -webkit-gradient(linear, left top, left bottom, from(#FFF), to(#999));
    background-image: url("arrow.png"), -webkit-linear-gradient(top, #FFF, #999);
    background-image: url("arrow.png"),    -moz-linear-gradient(top, #FFF, #999);
    background-image: url("arrow.png"),         linear-gradient(to bottom, #FFF, #999);
    background-position: -webkit-calc(100% - 6px) 50%, 0 0;
    background-position:    -moz-calc(100% - 6px) 50%, 0 0;
    background-position:         calc(100% - 6px) 50%, 0 0;
    background-position: right 6px top 50%, 0 0;
    background-repeat: no-repeat;
    border: 1px solid #999;
    border-radius: 3px;
    color: #000;
    display: inline-block;
    font: 16px/24px "Helvetica", "Arial", sans-serif;
    padding: 0 17px 0 6px;
    text-decoration: none;
}

View Demo: Right Start Background Position – Demo4

100%(right)から6px分引いたやり方。あんだけcalc()使ってたのにここを見逃すとは…。ばかが!あとはもっとスマートに4値構文を使うというやり方もあった。ので追加。Chromeも26.0から4値構文に対応するみたいだし。

あとどうでもいい解説。linear-gradientはto bottomが正。

Re: CSSで背景画像の配置を右から指定する方法 – ひあ的な生活。

-webkit-linear-gradient(top, #FFF, #999);をコピペしてたから全部topになってた…ので修正しました。

これで解決かと思いきや、iOS 5とかAndroid 4.2とかだとcalc()に対応していないので、やっぱりafter擬似要素を使うか画像自体に(今回の場合)6px分の透明な領域を追加して右から指定するとかかな。

で、今書いてて思ったのが、iOS 5とかでcalc()対応してないなら、自分が書いたafter擬似要素の記述もダメじゃん!ってことで、書き直し。多分これでOK。

.hoge a {
    background-image: -webkit-gradient(linear, left top, left bottom, from(#FFF), to(#999));
    background-image: -webkit-linear-gradient(top, #FFF, #999);
    background-image:    -moz-linear-gradient(top, #FFF, #999);
    background-image:         linear-gradient(to bottom, #FFF, #999);
    border: 1px solid #999;
    border-radius: 3px;
    color: #000;
    display: inline-block;
    font: 16px/24px "Helvetica", "Arial", sans-serif;
    padding: 0 17px 0 6px;
    position: relative;
    text-decoration: none;
}

.hoge a:after {
    background: transparent url("arrow.png") no-repeat 0 50%;
    content: "";
    height: 100%;
    position: absolute;
    right: 6px;
    width: 9px;
}

View Demo: Right Start Background Position – Demo5

a要素で指定したline-height:24px;と同じ高さの24px(height:100%;)にして、真ん中に持ってくるという方法。これでOKでしょう。

View Demo: Right Start Background Position – Comparison