読者です 読者をやめる 読者になる 読者になる

鈴木うどんの横須賀おもしろ生活

撮った写真や思ったことや技術ネタなど。出来るだけ大きなディスプレイで見ると良いと思う。ここでの発言は個人の見解であり、所属する組織の公式見解ではありません。

はてなブログのタイトルの文字列をCSSだけで画像に差し替える/更にアスペクト比を保持して可変幅にする

技術ネタ Blog Hack

f:id:UDONCHAN:20140113231335p:plain

はてなブログのタイトル部の文字列は、標準機能で画像をアップロードしてそっくり差し替えてしまったり、もとの文字列と一緒に表示したりすることができる。 けれども、僕にとっては、アップロードした画像に対して特定の解像度でのトリミングが強制されたりでイマイチ使い勝手が良いものではなかった。 そこで今回は標準機能をではなく別の方法でタイトル部の文字列を画像に差し替えようと思う。

まず、タイトル部分のHTMLソースを見てみると以下のような感じだった。

<header id="blog-title" data-brand="hatenablog">
  <div id="blog-title-inner" >
    <h1 id="title"><a href="http://blog.hateblo.jp/">鈴木うどんの横須賀おもしろ生活</a></h1>
  </div>
</header>

このHTMLソースに対して、タイトル部分に画像を表示させるためのアプローチは2つ思い浮かぶ。 ひとつはJavaScriptを用いて a タグの DOM要素 の innerHTML プロパティを画像に置き換えてしまうアプローチだ。 はてなブログJavaScript を自由に記述することが可能だから、このアプローチはストレートかつ楽だと思われる。 もう一方は、CSSを用いて文字列を画像に書き換えたように見せるアプローチだ。こちらは他のブログサービスでもよく見られる方法で、 いわばブログサービスにおける伝統芸能といったところ。ただし、可変幅に対応するのは正攻法では不可能で少しの工夫が必要となる。 今回は他のブログサービスにも応用できそうだということと、CSSの勉強がてらということで後者のCSSを用いるアプローチで取り組むことにした。

いきなり最終目標に向かうのはハードルが高いので、2つのステップを経て目標の達成に取り組むことにした。 まずステップ1でこれまで一般的に行われてきた方法を確認し、その方法をいくらか改善する。 そして、ステップ2で従来あまり取り組まれていなかったと思われる、アスペクト比を保持可能な可変幅のタイトル画像の実現取り組む。

ステップ1:文字列をはみ出させて背景画像を挿入する

可変幅にしないのであればステップ1だけで十分だ。

最も簡単な方法としてはdiv#blog-title-innerに幅と高さと背景を指定してh1#titleを非表示にする方法が考えられるだろう。

header#blog-title div#blog-title-inner{
    width:900px;     /* タイトル画像の幅 */
    height:360px;    /* タイトル画像の高さ */
    background:url('http://example.com/blog-logo.png');
    margin:2em auto; /* 上下にそれぞれに 2em の margin を設定して左右中央寄せ*/
}
header#blog-title h1#title a{
    display:none;
}

この方法には重大な欠点があって、a 要素が非表示となってしまうのでリンクとしての機能が失われる。 つまり、タイトル部をクリックしてブログトップへページ遷移ができなくなってしまう。これは問題だ。 この問題に対する従来からのよく用いられた対処方として、 a 要素のプロパティをdisplay:blockとしてブロック要素にしてかつ、内部の文字列を表示領域外に はみ出させるアプローチが用いられてきた。

header#blog-title div#blog-title-inner{
    width:900px;    
    height:360px;   
    margin:2em auto;
}
header#blog-title h1#title a{
    display:block;
    background:url('http://example.com/blog-logo.png');
    text-indent:-9999px; /*表示領域外に文字をはみ出させる*/
}

ただ、近年ではこのようなtext-indentプロパティに極端な値を設定する行為は、 SPAMサイト等で記事の内容に関係ないキーワードを本文に埋め込む手法としてよく知られているため SEOの観点からこの手法を用いるのは好ましくないとされているようだ。 そこで、本当に効果があるかは若干疑わしいが、 以下のようにtext-spaceプロパティを用いて同様の効果を別の記述で試みた結果、 実現可能であることが確認できた。

header#blog-title div#blog-title-inner{
    width:900px;
    height:360px;
    margin:2em auto;
}
header#blog-title h1#title a{
    display:block;
    background:url('http://example.com/blog-logo.png');
    text-indent:100%;   /* 段落はじめで要素の幅分だけインデントする */
    white-space:nowrap; /* 要素からはみ出しても改行しない */
    overflow:hidden;    /* 表示領域からはみ出した部分を隠す */
}

ステップ2:アスペクト比を保つようにしながらブロック要素を可変幅にする

通常の画像、例えばimg要素ではCSSheightプロパティをautoに設定しておくだけでwidthプロパティを可変幅としてもアスペクト比が固定される。 これはimg要素が含んでいる画像の解像度情報をベースにとすることによって、レンダラがアスペクト比を保って描画することを可能としている。

一方でブロック要素はimg要素と違ってベースとなる解像度を持っておらず、当然アスペクト比も未定義であるから、可変幅でのアスペクト比の固定は基本的には不可能である。 上で実現した一見画像のように見えているタイトル部分は、画像ではなくブロック要素であるため、つまり、基本的には可変幅でのアスペクト比の実現は少々困難だ。

では固定幅で満足かといえばそんなこともなく、特に最近一般的となっているレスポンシブデザインえはなおさらで、 様々な解像度で快適に閲覧可能なWebサイト実現のために、要素を可変幅で正常に表示させるというのは課題であると考えられよう。 方式としては css の media-query を用いる手法もあるだろうが、細切れに解像度ごとの外観を考慮するよりは可変幅のほうがいくらか手軽だろう。

今回は少しの工夫を加えることによって可変幅のブロック要素のアスペクト比固定を実した。 基本的なアプローチは、アスペクト比を保つ高さを持つ擬似要素をCSSの挿入だ。 ただ、それだけでは、もともとのa要素が持つ高さがの影響を受けてしまうため、 こちらはdisplayプロパティをabsoluteとすることによって親要素への高さの影響を無効にしている。

文章に書き下すと非常にややこしいように見えるが、以下に示す具体例を見るとそれほど難しいことをやっていないことが分かると思う。

header#blog-title div#blog-title-inner{
    position: relative; /* 子要素は相対配置 */
    width:55%;          /* 可変幅に設定 */
    max-width:900px;
    max-height:360px;
    margin:2em auto;
}
header#blog-title h1#title a{
    position:absolute;      /* 絶対位置に指定することによって親要素の高さを0にする */
    top: 0px;
    left: 0px;
    bottom: 0px;
    right: 0px;
    display:block;
    background:url('http://example.com/blog-logo.png');
    background-size:cover;  /* 要素全体を背景画像で埋める */
    text-indent:100%;
    white-space:nowrap;
    overflow:hidden;
}
/* ブロック要素が5:2のアスペクト比になるように40%の幅分の高さの擬似要素を挿入する */
header#blog-title div#blog-title-inner:after{
    content:"";
    padding-top:40%; 
    display: block;
}

所感

今回紹介した「CSSを用いて文字列を画像に置換する」という機能は、まったくおかしい機能という訳でもないと思うし、需要もありそうだしだから標準機能として存在しても良いのではと感じた。 まず、この機能は、一見img要素のalt属性を記述するのとほぼ同じではないかという反論がありそうだが、 画像が存在していることありきかどうかという点でセマンティクスが異なると考えられるし、需要という話においてもブログシステムに代表されるCMSにおいては少ないものではないと推測される。

それにもかかわらず、CSSの標準仕様として存在していないのはなぜだろうか。はじめは例えばCSSによる文字列の画像への置換はこのエントリ中盤にも挙げた通りSPAMサイトなどで悪用される危険性があるから、 敢えて実装されていないのではないかと思った。しかし、今回のエントリで挙げた通り、同様の外観を提供するためのHackは如何様な方法でも実現できるてしまう。結局その手の対策はイタチごっこに過ぎず無意味であると思える。 さらに、WCAGあるひとつの記事で、 今回と同様の目的を実現するのに、上で紹介した方式の基本的なものに類する、文字を外にはみ出させる方式が紹介されていて非常にもんにょりとした気分になった。

結局、多くのWeb開発者はプラットフォームによって Dirty Hack を強いられているのではないかという仮説に収束してしまった。 もしかしたら、Web開発者の現場と標準化団体の間に大きな隔たりがあるのではないだろうか。 僕はどちらにも属していないので印象に基いて述べることしかできないが、 Web開発者は過去にIE6の仕様に振り回された経緯などもあって、ある程度 Dirty Hack で凌いでしまう癖があるのではという気がする。 この辺り、詳しい人の意見が欲しいと思った。(意見が欲しいと書いて意見がもらえたことは皆無だが…)