font-family 指定を見る側が選べるようにしてみる

ある料理店があった。料理は「コックおすすめのワイン」とセットだった。

ある日コックは考えた。「人はみな自分好みのワインがあるに違いない。俺の好みを押し付けるのはやめてみるか」。それ以降、この料理店は「ワイン持ち込み自由」になった。「これでお客さんはもっと料理を楽しんでくれるだろう」。

ところが大半の客は持ち込みなどしない。料理の前に出される水と一緒に食べるだけなのだ。コックは愕然とした。「俺の料理を、まるで流し込むように水で食べるなんて」。

また以前のやり方に戻すことにした。必ず「おすすめワイン」で食べてもらう。多くの客は喜んだ……かどうかわからない。どうでもよかったのだ。もし聞かれれば「そう言えば水よりはこのワインのほうが、料理がおいしく感じられるね」と答える。だが、あっちの店に行ったときに茶が出されるのなら茶、ビールならビールにするだけだ。自分で選べるなどと考えたこともなかった。だからどこでどう手に入れるかどころか、ワインの自分の好みすら知らなかったのだ。

コックは悩んだ。多くの客が水で食べるよりましな状態にはなった。しかしごく少数とは言え、各々こだわりのワインを持ち込んで料理を楽しんでくれていた客はどうなるのか。いちばんよくわかってくれている彼らの思いを踏みにじるのか……。

フォントを指定しないこと

以前、「フォントの指定をやめる」と題して記事を書きました。そのときには

「本文は明朝系」という信念の人 (私だ) はブラウザのデフォルトを serif に設定しているだろうし、Windows でも自分の好みの表示になるようにがっちり設定している人には、そのフォントで読めるようにしてあげればいいではないか。

という訳で、このページの本文の font-family の指定はやめることにした。

と考えていたのです。

数年ぶりに WordPress のテーマを変えてみて、それが元々は欧文向きとは言え、文章を読ませるのに適したデザインでセリフ系のフォントを指定してあったこと、そのフォントを入れ替えてみようとしたことから、いろいろと考えながら調べているうちに、「Windows でも自分の好みの表示になるようにがっちり設定」することがほとんど不可能らしいことに気がつきました。

私は長いこと、デスクトップに Debian を使っています (Windows はどうしてもそれでしか動かないソフトを使う必要があるときだけなので、その際の見かけなど細かいことはまったく気にしないことにしています)。現在の Debian なら fontconfig で、たとえば

<?xml version="1.0"?>
<!DOCTYPE fontconfig SYSTEM "fonts.dtd">
<fontconfig>
    <match target="pattern">
        <test qual="any" name="family">
            <string>serif</string>
        </test>
        <edit name="family" mode="prepend" binding="strong">
            <string>IPAexMincho</string>
            <string>DejaVu Serif</string>
        </edit>
    </match>
</fontconfig>

のように設定することで、総称名 serif だけが指定されたときに使われるフォントを設定できます (もっと正しい書き方があるかもしれませんが)。

これでも随分と簡単になったものです。以前は defoma とかいうのがあって、その hints という設定ファイルを書くのがものすごく面倒でした。TrueType のフォントファイルを所定のフォルダに置くだけですぐに使える Windows などがとても羨ましく思えたものでした (Mac はよく知らない)。

それが周回遅れでトップに立ってしまったようなものです。Windows では「serif という総称名で表示される実際のフォントを設定する方法」が (少なくとも簡単には) ないのです (Mac はよく知らない)。web ブラウザーで設定できるかと思ったら Windows10 標準の Edge では、それすらもできないようです。

それでも Firefox では about:preferences#content の「フォントと配色」の詳細設定で、また Chrome では chrome://settings/fonts の詳細設定で、だいぶ設定できそうです。Safari はよくわかりませんが、ユーザー定義のスタイルシートを書けばできるらしいです。そういえば Firefox でも、userContent.css というユーザー定義のスタイルシートでもできそうですし、Chrome でも拡張機能でユーザー定義スタイルシートを使えそうです。

しかし、そこまでしてデフォルトのフォント設定をしている閲覧者はどれほどいるのでしょう。結局、「フォントの指定をやめる」というのは、そのようにデフォルトの設定も行っていない“意識の低い”閲覧者に対して何も与えないものでした。

フォントを指定すること

では、サイト制作者側できっちりフォントを指定してやればいいのでしょうか? ちょっと検索すれば「font-family 指定のおすすめ」のような記事がたくさん見つかります。たとえば

body {
font-family: -apple-system, BlinkMacSystemFont, "Helvetica Neue", "Yu Gothic", YuGothic, "ヒラギノ角ゴ ProN W3", "Hiragino Kaku Gothic ProN", Arial, "メイリオ", Meiryo, sans-serif;
}

みたいにするのがいいのでしょうか?

今度は“意識の高い”閲覧者を切り捨てていることになってしまいます。「sans-serif ゴシック系より serif 明朝系」とか、ヒラギノと游のどちらがいいかはほんとうに好みによりそうだし、さらにはもっといい (自分好みの) フォントを自分でインストールしてそれをデフォルトに設定している閲覧者に対して、その意図を無にしてしまうことになると思うのです。

閲覧者が選べるスイッチ

ブラウザーでもっと手軽にフォントを切り替えるボタンがついていればいいのでしょうが、現実にそういうものはありません。ズームは割と簡単にアクセスできるメニューやキーボードショートカットが用意されているのに。そういえば、そういうものがあるのにコンテンツの側で「文字の拡大・縮小」を用意しているサイトもときどき見かけますね。……というところで、そうかフォント切替スイッチをコンテンツ側で用意するという方法もあるのか、と思いつきました。

まずは「明朝体/ゴシック体の切替」の二択のボタンです。Javascript の element.classList.toggle を使えば簡単です。

CSS のほうで

body {
        font-family: Georgia,Times,"Times New Roman","YuMincho","Yu Mincho","Hiragino Mincho ProN",serif;
}
.sans {
        font-family: -apple-system,BlinkMacSystemFont,sans-serif;
}

のように、<body> に class=”sans” のない場合とある場合の font-family 指定を書いておき、HTML のほうには

<button id='toggle-font-button' title='明朝体/ゴシック体の切替' onclick='(function(){document.getElementsByTagName("body")[0].classList.toggle("sans");})()'>フォント切替</button>

をどこかに追加すれば済みます。

WordPress での実践

【2018年10月10日追記】Google Web FontsNoto Serif JP, Noto Sans JP が利用可能になったので、それを追加 (優先) しました。

「ヒラギノ明朝優先」「游明朝優先」……と選択肢を増やすときは、CSS で用意する class を増やして、HTMLでは element.classList.toggle の代わりに element.classList.removeelement.classList.add を使います。

style.css で

body {
        font-family: Georgia,Times,"Times New Roman","Noto Serif JP","YuMincho","Yu Mincho","游明朝","Hiragino Mincho ProN","ヒラギノ明朝 ProN",serif;
}
.serif {
        font-family: Georgia,Times,"Times New Roman",serif;
}
.noto-serif {
        font-family: Georgia,Times,"Times New Roman","Noto Serif JP","Hiragino Mincho ProN","ヒラギノ明朝 ProN","Yu Mincho",YuMincho,"游明朝",serif;
}
.hiramin {
        font-family: Georgia,Times,"Times New Roman","Hiragino Mincho ProN","ヒラギノ明朝 ProN","Noto Serif JP","Yu Mincho",YuMincho,"游明朝",serif;
}
.yumin {
        font-family: Georgia,Times,"Times New Roman","Yu Mincho",YuMincho,"游明朝","Noto Serif JP","Hiragino Mincho ProN","ヒラギノ明朝 ProN",serif;
}
.sans, .sans-serif {
        font-family: -apple-system,BlinkMacSystemFont,sans-serif;
}
.noto-sans {
        font-family: -apple-system,BlinkMacSystemFont,"Noto Sans JP","Hiragino Kaku Gothic ProN","ヒラギノ角ゴ ProN","Yu Gothic",YuGothic,Meiryo,sans-serif;
}
.hirago {
        font-family: -apple-system,BlinkMacSystemFont,"Hiragino Kaku Gothic ProN","ヒラギノ角ゴ ProN","Noto Sans JP","Yu Gothic",YuGothic,Meiryo,sans-serif;
}
.yugo {
        font-family: -apple-system,BlinkMacSystemFont,"Yu Gothic",YuGothic,"Noto Sans JP","Hiragino Kaku Gothic ProN","ヒラギノ角ゴ ProN",Meiryo,sans-serif;
}

のように用意しておき、functions.php で

add_action( 'get_footer', 'fontSwitcher' );
function fontSwitcher () {
?>
<script type="text/javascript">
  var body = document.getElementsByTagName("body");
  function resetFontClass () {
        body[0].classList.remove("serif", "noto-serif", "hiramin", "yumin", "sans", "noto-sans", "hirago", "yugo");
  }
  function addFontClass ($class) {
        resetFontClass();
        body[0].classList.add($class);
  }
</script>
<div id="fontswitcher">
  «
  <form id="fontswitcherform">
        <input type="radio" name="fontswitcher" onClick="addFontClass('serif')"><span class="serif">serif</span><br/>
        <input type="radio" name="fontswitcher" onClick="addFontClass('noto-serif')"><span class="noto-serif"> Noto Serif</span><br/>
        <input type="radio" name="fontswitcher" onClick="addFontClass('hiramin')"><span class="hiramin"> ヒラギノ明朝</span><br/>
        <input type="radio" name="fontswitcher" onClick="addFontClass('yumin')"><span class="yumin"> 游明朝</span><br/>
        <input type="radio" name="fontswitcher" onClick="addFontClass('sans')"><span class="sans"> sans-serif</span><br/>
        <input type="radio" name="fontswitcher" onClick="addFontClass('noto-sans')"><span class="noto-sans"> Noto Sans</span><br/>
        <input type="radio" name="fontswitcher" onClick="addFontClass('hirago')"><span class="hirago"> ヒラギノ角ゴ</span><br/>
        <input type="radio" name="fontswitcher" onClick="addFontClass('yugo')"><span class="yugo"> 游ゴシック</span><br/>
  </form>
</div>
<?php
}

としました。add_action( 'get_footer' )で最後のほうに付け加えました。スイッチのデザインは style.css で行います。

こういうものを作ってここに付けてみましたが、他で似たようなものを見かけたことはまったくありません。必要ないから流行らないのか、はたまた弊害があるのかもしれません。

***

コックが考え出した新しいやり方はこうだ。

何も言わない客には「おすすめワイン」をセット。だがちょっとだけ好みがある客にはテイストの違ういくつかの中から選んでもらおう。そして知識もあり手間も厭わぬこだわりの客には自分のワインで楽しんでもらおう。

さあ、この料理店は繁盛するだろうか……。

特定の文字だけ別のフォントを使う

まとめ

まとめを先に書いておきます。

特定の文字コードのみ(ここでは「除算記号」(U+F7))別のフォントを使いたい場合、まずそれについて定義します。

  @font-face {
    font-family: 'division sign';
    src: local('Georgia'),local('Times'),local('Times New Roman'),local('Serif');
    unicode-range: U+F7;
  }

その後に全体で使うフォントを定義します。ここでは Google Fonts から読み込む CSS で

...
/* latin */
@font-face {
  font-family: 'Lora';
  font-style: normal;
  font-weight: 400;
  src: local('Lora Regular'), local('Lora-Regular'), url(https://fonts.gstatic.com/s/lora/v10/4vqKRIwnQQGUQQh-PnvdMA.woff2) format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215;
}
...

などです。それらの後で、実際に使用するフォントを指定します。

body {
	font-family: "division sign",Lora,Georgia,Times, ... ;
}

この定義と指定の順番を入れ替えてはなりません。

web フォント Lora のうち「除算記号」だけ使わない

結論を先に書いた後でゆっくり経緯を書きます。

このブログでの欧文フォントに Google の web フォント Lora を使うことにしました。デザインがとても気に入っています。

ところでこの Lora で「除算記号」は、真ん中の水平線がなく、コロン「:」にそっくりのデザインになっています。除算記号としてこの形のものを使う地域がある、ということを聞いたことがあります。

Lora の除算記号÷
Georgia の除算記号÷

その違いがうまく表示されているでしょうか。

通常の文章なら除算記号などめったに出てくるものでもなく、たいした問題ではないでしょう。ところが、このブログ「半月記」での一番人気の記事は

なのです。ここで除算記号が、日本でおなじみの形ではなく、すべて「:」そっくりになっていたら、読者は訳がわからなくなってしまいかねません。これらの記事の中の主張のひとつが「除算記号などごく限られた領域でしか使われないのだ」なのですが、その内容を書くためには除算記号を使わなければならず、そのデザインにまでこだわらなければならないとは、なんと皮肉なことでしょう。

@font-faceunicode-range

全体的には Lora をたいへん気に入っているので使い続け、「除算記号」のみ別のフォントで表示するようにしてみます。

CSS の @font-face には unicode-range という記述子がありますので、これを使います。このリンク先の例にあるのとほぼ同じように

  @font-face {
    font-family: 'division sign';
    src: local('Georgia'),local('Times'),local('Times New Roman'),local('Serif');
    unicode-range: U+F7;
  }

のように書きます。文字コードU+F7(つまり「除算記号 division sign」)の場合のみ、Lora ではなく、Georgia, Times, “Times New Roman”, Serif のどれかを使うようになります。

定義の順序

さて、そもそも web フォント Lora を使うためには、CSS に定義 @font-face があるはずです。ここでは外部 CSS として Google Fonts より読み込んでいます。

これらの定義があれば

body {
	font-family: "division sign",Lora,Georgia,Times, ... ;
}

のように使用フォントを指定して、意図通りになるはずでした。しかし実は、それらの記述の順序が大事でした。まず局所的なフォント定義(‘division sign’)が最初、それから全体のフォント定義(Lora)、最後にフォント指定、の順番でなければなりません。

WordPress での実践

ここでは WordPress の子テーマを使っています。子テーマの style.css のはじめのほうに局所的なフォント定義(‘division sign’)を書いても、それより前に外部 CSS の読み込めば、正しい順番になりません。もし 子テーマの style.css より後に外部 CSS 読み込みをもってきても、やはり正しい順番にはなりません。

そこで局所的なフォント定義を分離して先に読み込むようにします。この部分はたいした量でもなく、あとで書き換えることもまずないので別ファイルにせず、functions.php に直接書いて読み込ませることにしました。そして優先度を上げ(10より小さく)ます。

add_action( 'wp_head', 'division_sign_font', 7 ); /* 8 だと遅すぎる */
function division_sign_font() {
?>
<style type='text/css'>
  @font-face {
    font-family: 'division sign';
    src: local('Georgia'),local('Times'),local('Times New Roman'),local('Serif');
    unicode-range: U+F7;
  }
</style>

あとは、前の記事に書いたように、外部 CSS の読み込みと子テーマの style.css の読み込みの順番が正しくなるように優先度を付けます。

親テーマ指定の web フォントを子テーマで変更する

まとめ

まとめを先に書いておきます。

<head> 内で

<link rel='stylesheet' id='libre-lora-css' href='https://fonts.googleapis.com/css...' ... />
...
(親テーマ指定のwebフォント用CSSの削除)
...
<link rel='stylesheet' id='libre-2-style-css' href='...' ... />

の順になってほしいため、子テーマの functions.php では

add_action( 'wp_enqueue_scripts', 'libre_kid_fonts', 9 ); /* style.css より先に読み込む必要がある */
function libre_kid_fonts() {
        wp_enqueue_style( 'libre-lora', libre_lora_url(), array(), null );
}
add_action( 'wp_enqueue_scripts', 'libre_dequeue_google_fonts', 11 ); /* 優先度を落とす */
function libre_dequeue_google_fonts() {
        wp_dequeue_style( 'libre-2-fonts' );    
}

のように、add_action('wp_enqueue_scripts') に優先度を付ける必要があります。

WordPress のテーマ Libre 2 と Google web フォント Lora

では、結論を先に書いた後でゆっくり経緯を書いてみます。

ふと WordPress のテーマを変えてみました。もう自分で作る気力もないので、公式ディレクトリで人気順に見ていって、シンプルな Libre が気に入りました。すぐ後に WordPress.com のほうで後継 Libre 2 を見つけ、「ダウンロードしてインストール型 WordPress で使用できます」とあったので早速そうすることにしました。

いくつか直したいところがあったので、子テーマで手を入れることにしました。変更点を備忘録的にここに書いておくと、

  • フォント関連の変更。欧文フォントを Libre Baskerville からLora に変更した。
  • 装飾に Dashicons を使うことにした。
  • 記事の最初のメタ情報にカテゴリー・タグを表示することにした。記事の最後には表示しない。
  • 本文の段落は1字下げ。段落間は小さくする。箇条書きの上下のマージンも小さく。
  • 本文内の見出し(<h3>など)は上下にマージン。ただしその見出しが本文先頭なら上マージンなし。

このテーマは、やや長めの文章を見せるのに適するようにセリフ(serif)の書体 Libre Baskerville が指定してありました。そこで日本語で書かれるこのブログでも、セリフ(明朝体)にすることを考えます。ただ、よくあるヒラギノ明朝体や游明朝体の中に混ぜてみると Libre Baskerville は落ち着かない感じがして、同じく Google web フォント の Lora に変更することにしました。

wp_dequeue_style() の実行順序

親テーマの Libre 2 は、その functions.php で

function libre_2_scripts() {
        ...
        wp_enqueue_style( 'libre-2-fonts', libre_2_fonts_url(), array(), null );
}
add_action( 'wp_enqueue_scripts', 'libre_2_scripts' );

と、webフォント Libre Baskerville 用の CSS を google から読み込むようになっています。子テーマでは、ここで enqueue しているものを dequeue すればいいのです。

が、子テーマの場合、そもそも functions.php の実行順は (1)子テーマの functions.php (2)親テーマの functions.php です。したがって、子テーマの functions.php に普通に dequeue を書いてもこれが先に実行され(つまりまだ enqueue されていないものを dequeue しようとする)、その後に親テーマで euqueue されることになります。

そこで、子テーマでは dequeue の実行の優先度を下げて(10より大きな数字にして)書いておかなければなりません。

wp_enqueue_style() の実行順序

子テーマでは、親テーマの libre_2_fonts_url() を真似して、別のwebフォントである Lora 用の CSS を指定する libre_lora_url() を書きます。

この CSS は Google Fonts から読み込まれますが、内容は

...
/* latin */
@font-face {
  font-family: 'Lora';
  font-style: normal;
  font-weight: 400;
  src: local('Lora Regular'), local('Lora-Regular'), url(https://fonts.gstatic.com/s/lora/v10/4vqKRIwnQQGUQQh-PnvdMA.woff2) format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215;
}
...

のような @font-face がいくつも並んでいるものです。子テーマの style.css では

body {
	font-family: Lora,Georgia,Times, ... ;
}

のように、この定義を利用して使用フォントを指定します。

ところでこの 定義 @font-face と指定 font-family の順序ですが、ブラウザー Chromium の場合 (Debian の Chromium で確認しました。おそらくどの OS でもブラウザー Chrome でも同じだろうと思います)、前もって @font-face がないと font-family での指定時にそれが存在しないものと扱われるようです。Firefox ではその順番は関係ないようでしたが。

そこで、子テーマの style.css より前にこの CSS が読み込まれるように、wp_enqueue_style() を呼ぶ add_action('wp_enqueue_scripts') の優先度を 9 にします。