ハイアクセシブルウェブサイトとウェブデザイン (フォント関連)

font-familyの指定

font-familyにシステムデフォルトのフォントを並べているのを非常によく見る。 例えばYahoo! Japan

MSN Japan

Google (google.co.jp)

まず先に言うならば、Arialは欧文書体であり、フォント解釈を厳密に行う環境の場合、Arialが指定されることで文字化けが発生する。 ここではArialがないというケースを除外して次のように処理される。

  • Arialにグリフがないフォントを置き換える
  • Arialにグリフがないためフォント自体を置き換える
  • font-family自体を解釈せず既定のフォントで表示する
  • Arialのみで表示しようとする (グリフのない文字は文字化ける)

このうち最も困るのは最後のケースだが(Arialを指定したがために文字化ける)、実際は欧文をArialで表示し、和文を他のフォントで表示して「美しい」と感じるのは難しい1。それよりは全部和文書体の方がバランス的にもマシである可能性のほうが高いのだ。そのため、最初のケースも明らかに意図的でない限りはまずい動作をする。2

昔、ごく限られた条件下では、フォントが指定されないことに起因して文字化けるということがあった。 これは、ブラウザがデフォルトフォントを欧文フォントに決め打ちしており、フォントレンダラーによる文字選択は可能なので、指定しなければフォントレンダラーに選択を任せないために文字化けるという状況であった。

しかし、現状で考えると

  • 日本語版のWindows/Macであれば和文フォントがあり、メジャーなブラウザは和文フォントを選択する
  • 非日本語版でデフォルトの和文フォントがないのであれば、指定したところでそのフォントはないので使えない
  • 日本語環境を無視しているようなブラウザをわざわざ使用するユーザーは文字化けすれば設定すると思われる

ということから、システムデフォルトのフォントを指定することは 現実的に意味がない

強いて言えば、ヒラギノを優先して指定することで、Windowsユーザーでもモリサワ書体を購入しているユーザーにはヒラギノを優先して使わせる、ということも可能だが、それは「ヒラギノが他のどんな書体よりも美しい」というMaccerの思い込みに過ぎない。3

Mimir Yokohamaでは次のように指定している。4

通常フォントをサンセリフ体、本文フォントはセリフ体を指定している。 セリフ体のSource Han Serif JPを除くと全て商用フォントである。 これは、もしユーザーが商用フォントを持っているのであればそれを使ってもらおうということである。 本来のfont-familyの使い方であると言える。

適当に選択しているわけではなく、本文フォントとなるセリフ体には流れるようなグリフで、十分に収録グリフがあり、長文を読むのに疲れないフォントであることを選択基準にしている。 サンセリフ体についてはいわゆる「丸ゴシック」である。「丸ゴシック」という抽象フォントがないため、イメージとしてより好ましいフォントを並べる、という方式をとっている。もしあるのであればsans-serifではなくrounded-sanscjk-rounded-gothicのような指定がしたいところである。

font-family指定がシステムデフォルトフォントを指定するというのは、「システムデフォルトフォントが嫌いで、わざわざ商用フォントを入れて設定しているユーザーに対してシステムデフォルトフォントを強制する」ということでもある。 これはユーザービリティを高めているのではなく損ねている。 そんなことに貴重な数十バイトを使用するべきではないのではないか。

webfontについて

webfontはCSS 3.0で追加された、ウェブリソース上に配置されたフォントをダウンロードして使用するものである。 font-familyがシステムフォントしか指定できないために、結局システムデフォルトフォントばかり指定するという無意味に近い状況がこれによって改善された。

なんでこんなものが追加されたかというと、従来デザイン上特定のフォントを使用したい場合にどうしても「画像を使う」という状況であったが、W3Cとしては常に文字情報であればテキストとCSSを使うべしという思想である。 これを実現するためにCSSにテキストを重ねたり傾けたりして配置する機能と共に、特定のフォントを使わせる方法を提供したものである。

「デザイナーの意図するフォントを指定できるようにした」と解釈される場合も多いが、実のところそれは副次的なものであり、W3Cがこの仕様を制定した意図としては「テキスト画像を撲滅したい」ということであり、既にテキストとして表示されている情報がどのように表示されようが(W3Cにとっては)どうでも良いのだ。

だが、実際のところW3Cの意図に反して画像撲滅よりは従来からテキストで表示されていた部分のデザイナーの意図反映に使用されている5

このために「確実に好きなフォントが使える」程度の理由でwebfontが指定される傾向があるのだが、それはデザイナーの完全なエゴである。

欧文フォントであれば(W3Cが考えたように)ロゴ画像をダウンロードするのとさして差のないデータ量であり、それほど躊躇われるものではないのだが、和文フォントであると常用漢字のみ収録のものですら2MB程度ある。画像などよりもはるかに大きく、通信料金で考えると2円ほどかかる。 これを表示ごとに要求することになるのだ。6

しかも、そもそもフォントレンダラーは一種のバイトコードインタープリタであり、潜在的にはフォントファイルはウィルスとして機能しうる7

また、そうでなくてもテキストと異なるグリフを持つフォントをダウンロードさせることにより、コンピュータ(例えばGoogleやSymantecのチェック)を欺いてユーザーに嘘の情報を見せるサイトを作ることもできる。 これは詐欺などにおいて有効な手法である。

にも関わらず、現状主だったモバイルウェブブラウザにおいてはwebfontを無効化する機能というのはない。 私も仕事にしていたこともあるくらいだからフォントは大好きなのだが、それでも自分の望むデザインのためならユーザーに負担を強いたり、不快な思いをさせても構わないというデザイナーのエゴには賛同できない。

長文のための工夫

私が書くコンテンツは(この文章を含めて)往々にして長文である。

これは、意味ある情報を提供してこそ意味があるという思想に基づくもので、内容空疎なイメージ戦略や思考停止で選ばれるものを提供しようとは思っていないから…なのだが、長文を含めて読むのが得意な人は比較的少数だし、やはり長文を読むのはどうしても負担になりやすい。

そこで、可能な限り長文を読みやすいように心がけている。

本文に対する設定としては

  • フォントは細めで、流れるような(連続した文字を少ないコストで読める)明朝体
  • 自動カーニングで行間を詰める(長文の流し読みが楽になる)
  • これだけでは「文字量が多く黒い」と感じてしまうため、少しだけ文字間を空ける
  • 黒さを軽減するため、行間を少し空ける
  • 結果として段落間の行間が詰まってみえるため、段落間に空白をもたせる

このほか、リズム感のある句読点や、かぎかっこの使い方、言い回しの選択などの技法と組み合わせてできる限り「楽しく読める長文」を心がけている。

ウェブサイトは内容であって見てくれではない、ということを忘れてはいけない。


  1. この指定は「欧文のみの要素に対して欧文フォントを適用する」指定ではない。和文フォントが適用される条件でもArialのグリフがあるものはArialが使われるのだ。

  2. もちろん、欧文書体を和文書体に含まれていない特定のフォントを選択してほしいという理由で意図的にこのようなことをすることもできるが、その場合は和文書体よりも先に欧文書体を書くべきで、MS PGothicよりもArialが後、というのはArialの欧文よりMS PGothicの欧文のほうが好ましく、けれど和文書体にある欧文ではなくArialを使ってほしい、というよくわからない美的センスである。

  3. モリサワ書体を含め、商用フォントを選択肢とするのであれば、無条件に「ヒラギノが最高」ではなく、数多のフォントからそのサイト、その文章に適切なフォントを選択できるはずだ。

  4. Chienomiでは既存のWordPressテーマを使用しているため、私は関知していない。

  5. これは当然の帰結と言える。デザインされた画像を作成するのと比べて、CSSでそれを実現するのは難しいし、同じフォントであってもレンダリングで見た目に差が出ることから望むほどデザインを再現してもらえない。記述量も多く、しんどい。

  6. キャッシュがあるから、というのは傲慢である。特にモバイル環境ではキャッシュを取り直す状況は非常に多く、セッションごとに取得すると考えて間違いない。

  7. このために以前のWindowsではフォントインストールにセキュリティリスクがあるという警告が出されていた。

PureBuilder Simplyと新しいサイトのお話

PureBuilder Simply

デザイン

PureBuilder Simplyは従来とは大幅に考え方を変えた。

従来は基本的に「いっぺんの処理ですべて行う」というものであった。 このため、特に難しかったのが、ACCSにおける「前後の記事へのリンク」である。

PureBuilder Simplyは処理が分離されている。 一番基準となるスクリプトはデフォルトで全記事を処理する。 そして、全記事のメタデータをデータベースに書き出すのである。

従来はPureDocメタセクションに直接書くという方法をとってきた。 そのため、ドキュメントが処理時に変更されるのだが、これは扱いにくい側面が結構あった。

従来のPureBuilderはドキュメントごとに処理するようになっており、 メタデータはドキュメントにある。 そのため、処理時には「そのドキュメントのメタデータしか知らない」状態であった。

今回のPureBuilderもすべてのドキュメントのメタデータを読み込むことはしないが、 すべてのドキュメントのメタデータの書き出しは行う。

メインスクリプト以外については、このスクリプトが書き出したデータベースに基づいて処理を行うため、 すべてのドキュメントのメタデータを知った状態で処理することができる。

Frontmatter

もともとPureBuilderはPureDocを前提としていて、Markdown supportはPureBuilder2で採用された。

Markdown Supportを採用したのはWindowser向けのフレンドリーシュガーであり、 Mimir Yokohamaでサービスを展開するにあたって、記述の難しいPureDocではなく、 記述しやすいMarkdownで書けるようにすることで「ユーザーによる更新の容易さ」を向上させる目的があった。

この時点でMarkdown YAML Frontmatterのことを知らなかった。 そのため、従来のPureBuilderではMarkdownドキュメント中にHTMLコメントを入れ、その中にPureDoc meta headerを入れるという仕様になっていた。

だが、そもそもMarkdownにYAMLでメタデータを書ける仕様である。 同じYAMLのメタデータなのだから、これに従うべきだ。

そのため、PureBuilder SimplyではMarkdownドキュメントのFrontmatterを必須としており、 frontmatterが存在する前提で処理する。 (titleメタデータが必須であるため、どのみちfrontmatterは必須となる)

さらにReStructured Textに対応したが、これも先頭にコメントを入れてYAMLでヘッダを書く、という仕様とした。 次のような具合である。

ReSTなら

Pandoc

PureBuilder Simplyはドキュメント処理をPandocにゆだねている。

これは別に手抜きというわけではない。従来にしてもMarkdownの処理はKramdownによって行っているのであり、 PureDocの実装は別なので特にPandoc採用でアウトソーシングしたという感はない。

Pandocの利便性と「標準性」から、「Pandocを主としてPureBuilderは補助であるほうがユーザービリティが高い」と判断したのだ。

PureBuilder Refinedでは新しいPureDoc Zをサポートする予定だが、 PureBuilder Refined自体がPureBuilder Simplyでは不足の場合に限られるので微妙かもしれない。

「事前生成」戦略

PureBuilderは言葉の意味としてはCMSで合っているのだが、どちらかというとCCS(Content construction system)に近い。 基本的に生成機能だけで、管理に関してはシステムの標準的機能でなるべく簡単てにできるようにしている。

一般的なCMSとの最大の違いは、「Webアプリケーションとして動作しない」ということだろう。 原則としてPureBuilderは(ホームページビルダーなどで書いた場合と同様に)ローカル環境でウェブページを組み上げ、そのままアップロードすることで動作する。

従来のPureBuilderにあった、Webアプリケーションとして動的に生成するためのプラガブルな仕様は現時点ではPureBuilder Simplyにはない。 予定もされていない。単一ページのサポートは限定的で、原則としてディレクトリ単位でビルドを行う。 全体ビルド機能も排除された。

より「サイトを構築するためのスクリプト」という点に特化した。

私のウェブコンテンツ生成は可能な限り「事前生成戦略」をとっている。 通常、ページは書く回数よりも読まれる回数が多いので、コストのかかる処理は1度だけ行いたい、という方式だ。

これは構造的に最速の方式でもある。

ウェブサイトの仕組みを考えればわかるが、たとえばWordPressでは

  1. ウェブサーバーが接続を受け付ける
  2. ウェブサーバーがPHPプロセスにWordPressを渡す
  3. WordPressがデータベースにアクセスする
  4. データベースから必要なデータを受け取ったWordPressがページを生成して返す
  5. ウェブサーバーが応答する

という手順になる。 WordPressのキャッシュ機能ではWordPressはデータベースから受け取った結果を処理せずにそのまま応答するため、高速化するのだが、 それでもこの接続にかかる時間が決して小さくない。

対して、静的ファイルならば

  1. ウェブサーバーが接続を受け付ける
  2. ウェブサーバーがファイルを読み込んで応答する

とプロセスが少なくなり、より高速だ。 WordPressの高速化手法も、PureBuilderが同様の手法を適用すればさらに高速する。

さらに、ウェブサーバーにとって静的ファイルの応答は基本的機能であるため、 より高速なウェブサーバーが選択できる、というメリットがある1

また、静的ファイルのみでウェブサイトの構築が可能なことで、ウェブサーバーの選択肢が増える。 PHPが動作しないGeocitiesやGitHub Pagesでも構築可能だ。

Mimir Yokohama新サイト

新サイト開設

Mimir Yokohamaの新ウェブサイト開設は、主に「異様にサイトが重くなっている」という事情からきているのだが、 実はそもそもPureBuilder Simplyはこの新サイト構築のために書き直した。

Aki SIE新サイト開設時もそのためにPureBuilder2を作ったのだが、 PureBuilder2が6ヶ月も要したのに対して、作業時間で4日、経過時間でも9日と非常に速く書き上がった。 やはりデザインは大事だ。

そして当然ながら、Mimir YokohamaウェブサイトがPureBuilder Simplyのデビュー戦となった。

「楽」という言葉の意味

Mimir Yokohamaのウェブサイトの出来には非常に満足している。

だが、この開発において“PureBuilder Simplyの開発”は全行程における30%程度でしかなかった。

PureBuilder Simplyを使ったサイトは非常に「楽」であり、「簡単」なのだが、誤解してはいけない部分がある。

今回はWordPressからの移行であるため、WordPressで利用している機能に近い状態を狙った。 デザイン的にも近く、バックエンドの変更や、それによって品質が低下した印象を与えないようにしている。

まず言っておくと、「WordPressのサイトを欲している」のであれば、PureBuilderを採用するよりも、 WordPressを採用したほうが確実に楽にできる。 なぜならば、そもそも欲しているのはWordPressなのだから、WordPressよりも楽にはならない。

そもそもPureBuilder Simplyによる構築と、WordPressによる構築はちょっと話が違うのだ。

WordPressの場合、テーマ機能やプラグインなどは既に揃っている段階から話をはじめる。 PureBuilder SimplyはそもそもWordPressではできない完全なカスタマイズを実現するという前提があり、 WordPressで言えばテーマやプラグイン、CSSなどを「すべて0から記述するところからはじめる」というのと同じ位置にある。

もしもWordPressをその位置ではじめるという形で条件を揃えたならば、PureBuilder Simplyのほうが「楽だ」と断言できる。

実際はそのような条件では比べないと思うのだが、逆にWordPressに合わせて構造やビルドの仕組み、テンプレートやCSSなどがすべて揃った状態からはじめるならば、 これもPureBuilderとしてはそこまで分の悪い勝負ではない。

なぜならばその状態で必要なことは

  • 文書を配置したい場所に新しくファイルを作り、MarkdownまたはReSTで書く
  • PureBuilderのバッチスクリプトを実行する
  • Gitでcommit&pushする

というだけのことだからだ。

「設定ファイルを書く」「コマンドを実行する」というあたりに、「Unixer的な楽さ」の感覚があるが、 これに関しても別にお客様に納品するときにはちょちょいと調整すれば良い話だ。 若干手順が多いようにも感じられるが、実際はWordPressの場合は

  • ログインページにアクセスしてログインする
  • ダッシュボードから新規記事の投稿を選択する
  • ページ上で文章を書き、タイトルやカテゴリなどを選択する
  • 投稿ボタンを押す

という手順で、慣れてしまえば麻痺するが、かなり手順は多い。 私が使っているWordPressサーバーは遅く、1クリックあたり1分とか待たされるので、 「WordPressは手順が多くて面倒」という印象が強い。 あちこち飛んではキーボードとマウスを持ち帰るUIも嫌いだ。

もし、PureBuilder Simplyでできることと相当のものとして、 「WordPressもデザインや挙動をカスタマイズする」 という条件にしたならば、これはPureBuilder Simplyが有利だろう。

PureBuilder Simplyを採用するのは「入り口」

だが、実際はPureBuilder Simplyの採用というのは全行程においてそこまで大きなものではない。 おそらく、PureBuilder Simplyのデフォルトで作られたサイトには誰もが失望するだろう。

PureBuilder Simplyの場合、「テンプレート」と「CSS」という要素がある。 これを書くのは、PureBuilder Simplyで「簡単にしている」部分ではないところであり、技術者の腕のみせどころだ。

最近はwebといってもCMSありきのエンジニアが増えていて、HTMLやCSSを直接記述することができない人が増えている。 だが、それはCMSの枠の中でしか仕事ができていない。 PureBuilder Simplyはそれをイチから作るのであり、ウェブデザイナーとエンジニアの腕の見せ所だ。

PureBuilder SimplyのテンプレートはPandocのHTMLテンプレートである。 ただし、「Pandocの出力に対してeRubyで処理できる」ようになっている。 結果、テンプレートの展開は2度行われる。

Pandocのテンプレート機能は意外なほど強力だが、 「値が○○だったら」という式はかけない。

eRubyは完全なプログラムであり、「なんでもあり」である。

比較的単純な(PHPやSSLで行うような)ものに関してはPandocテンプレートで処理できる。 実際、現時点でMimir YokohamaのウェブサイトはeRubyでの処理を無効にしている。

PureBuilder Simplyにおけるテンプレートの作成は「若干の、もしくは本格的なロジックを含むHTMLテンプレートを書く」という作業である。 もちろん、それはデザインが必要であり、CSSと連動した「ページ設計」が必要となる。 ページ設計はかなりエンジニアリングレベルの高い作業だし、 最も単純にはデフォルトテンプレートを使っても良いのだが

CSSと連動した高度なロジックを使いたいといった場合には難易度は上がる。

プログラミングではないけれど、ロジカルに考える

もちろん、CSSを書くのはプログラミングに近い次元になってくる場合もある。

今回のMimir YokohamaのウェブサイトはハンバーガーメニューとドロワーについてはJavaScriptを一切使用していない。 これはAki SIEウェブサイトで使用されていたものの改善版である。

だがそのために、配置や仕組みに関してはかなりロジックが含まれている。 CSSにはロジックはほとんど含まれないが、「ロジックをほとんど含むことのできないものによって動的なフローを実現する方法」を考えるところにロジックが行使されているのだ。

プロモーションモードのページでは下からフェードインするようなページデザインになっているが、 これもJavaScriptは使用していない。

これは単純にHTMLとCSSで実現するウェブサイトを構築するときに、 「単に書くのではなく、そこにロジックを含んだ構造をもたせる」ときに必要となるものだ。

PureBuilder Simplyはウェブサイト構築ツールであって、 デザインに関しては感知しない。 だから、そこに関しては「自分で作る」必要があるのだ。

今回も難易度や作業量としては、テンプレートとCSSを書くことのほうが大きかった。

PureBuilderはサイトの内容については感知しないのだから、別にJavaScriptを含んでもいい。 特にそれを禁止はしていないし、支援もしていない。 別にWordPressのクローンを作ることも、できなくはないのだ。

「サイトを構築する」手間で考えると…

単純に「見栄えするサイトを作りたい」という条件で考えた場合、 WordPressのほうがずっと楽だ。 なぜならば、デザインやロジックが既に存在していて、それを選ぶだけだからだ。

PureBuilderが担っている部分に関してはWordPressよりも楽かもしれないが、 WordPressにはあるがPureBuilderにはない部分がある。 それについてはどうしてもWordPressでは発生しない労力を払わざるをえない。

だから、サイトを構築する手間がWordPressよりも軽いということはない。 WordPressのインストールの労力を含めてもだ。

だが、そのような「敷居の低さ」を求める人がPureBuilder Simplyを自らインストールしてサイトを構築することは想定していない。 PureBuilder Simplyで自ら構築する人は、そんなものがなかったとしてもテンプレートを書き、CSSを書き、JavaScriptを書く人だ。 私がお客様にPureBuilderの簡便性を説くときは、そのような想定で話しているわけではない。 構築はこちらで行って、更新をお客様がされる際には難しくない、ということを言っているのだ。

テンプレートやCSSやJavaScriptは自分で書く、という前提のもとでは、PureBuilder Simplyの採用はかなり労力を削減することができる。 PureBuilder2と比べ管理の手間もかなり減り、非常に楽になった。

新サイトのデザイン、ポリシー

PureBuilderの強みは事前生成による高速性である。

さらにMimir Yokohamaでもポリシーとしている「軽量さ」と「アクセシビリティ」に重きをおいたデザインとなっている。

ただし、Aki SIEのウェブサイトは発想力をアピールする意図もあったのだが、デザインがちょっと古くさい2。 今回Mimir Yokohamaのサイトではよりモダンなデザインを採用することにした。

といっても、そのデザインはほとんどがWordPressで使用していたテーマのビジュアルを踏襲している。 ただし、全く同じというわけではなく、もとがシンプルなデザインであったために「ブロックごとに端までボーダーをひっぱる」「白に対してグレーのラインを引くモノトーン調」「右側サイドバーの2カラム」といったことを踏襲しているだけで、 メニューデザインなどは異なるし、実はカラーテーマも違う。 このあたりは「変えることが目的だった」わけではなく、そもそももとのブログでもカスタマイズしたかった部分なのだ。

ちなみに、ブログではそんなシンプルなデザインであるにもかかわらず、ちょっと古い環境(IEなど)では表示が崩れる。 なんでその程度のもので崩れてしまうのか、私には疑問でならない。

前述の通り、ハンバーガーメニューやフェードインテキストなどはCSSで記述されている。 これは、主に次のような意図だ。

  • JavaScript禁止の環境や、JavaScriptを搭載しないブラウザでもエクスペリエンスを損ねない
  • JavaScriptを使用していない分容量的に軽量
  • HTMLを「意味」を欠落させない

ただし、古いブラウザでのエクスペリエンスは若干犠牲になっている。 だが、それは「アニメーションが表示されない」程度の話で、表示自体はChrome 1.0, Firefox 1.7, Opera 9.0, Safari 3.1で対応している。 あまり言いたくはないが、IEは9.0からの対応だ。

実は今回のサイトではHTML中に同じメニューが2回登場する。これは、上記環境よりも古い環境においてはドロワーが表示できない代わりにサイドバーにメニューを表示させるようにするためだ。 これはアクセシビリティのための処置だが、場合によっては1kB程度はサイズが増加するため、ちょっと残念なところだ。 このメニューはごく初期のCSSしかサポートしない環境でも正常に表示される。 CSSを全くサポートしない環境の場合、メニューは2つ表示される。 この場合上のメニューは記事タイトルよりも上になり、高さもあるため見栄えしないが、そのような環境では仕方ないし、十分許容範囲だろう。 むしろタイトルと記事の間に入るよりはずっと良いと考えられる。

軽量性重視だが、従来と違い飾りとしての画像がところどころに入っている。 従来があまりにも殺風景すぎたという反省であり、このあたりはブログの時点で投入しているものを継承している。

プロモーションページの実現

全く印象の異なるプロモーションページだが、書き方はほとんど同じである。 別にテンプレートをかき分けることもしていないし、普通にMarkdownで書かれたページだ。

プロモーションページの印象の違いは、テンプレートにおける若干の工夫と、CSSによって実現されている。 frontmatterでプロモーションページであることを指定すると、Pandocテンプレートによってそれがクラス設定される。 あとはそのクラス設定に基づいてCSSで見せたくない要素を消去し、一部レイアウトを変更する。

こうした発想力は、やっぱりロジカルシンキングなのだ。

プロモーションページもかなりシンプルだが、 見出しには画像を使いたかった、という気持ちがある。

そうしていないのは、「適切な画像」の選定が非常に困難だったことと、 ヘッダーごとに画像を選択するためにはそれなりにあまりスマートでない余計なロジックを追加する必要があったからだ。

もしもこれぞという画像があれば採用することだろう。

ちなみに、プロモーションページはスマートフォンの商品プロモーションページをイメージしている。 なるべく説明的にならないようにしたのだが、後に書いたものほど説明的になってしまった。 どこまでイメージで語り、どこまで具体的に語るかは難しいところだ。

速度

実際のところ、Mimir Yokohamaにおける制作ポリシーと同一ポリシーで制作したのであり、「最速を目指す」という方式ではない。3

だがそれでも、旧WordPressサイトの10倍程度の速度になっている。 高速化のために使用した手段は以下のようなものだ

高速なサーバーを選択
ConoHaはSSDを採用しており、ネットワーク帯域も太く高速だ。 Nginxを採用
静的ファイルの応答に関してはNginxは最速に近い コンパクトなHTML
できるだけ冗長な表現を避けてコンパクトなHTMLが生成されるようにした。ただし、アクセシビリティを優先してメニューは重複している 可能な限りCSS
JavaScriptなしで動作するようにしたのは「JavaScript無効というオプションを与えるため」だが、ロジックの最適化によってある程度の容量削減に繋がっている WebP
WebPをサポートするのはChrome/Chromium系列だけだが、WebPは「アルファチャンネルつきロッシー圧縮」が可能なため、PNGよりもだいぶ軽くなる。一部PNG画像についてはobjectタグによりWebPを優先して使用するようにしている。アクセシビリティを重視し、重要な画像ではこの処理は行われずimg画像を使用している。 画像よりもCSS
「デザインはセンスでがんばる」方式で、画像などのアイテムの使用数は非常に少ない。寂しさはグラデーションで埋めているが、華やかさに欠ける感は否めない。

高速性ということでいえば、「CSSが重い」ということは感じるが、改善は若干難しい。 もちろん、minifyなどのテクニックである程度は小さくなるが、保守性を重視しているためだ。

また、CSSを3つから1つに減らすというアイディアもあったのだが、比較的サイズの大きいファイルであり、並列ダウンロードしてもらったほうが速いことから分割に戻した。

ミーミルのロゴなどは埋め込んだほうが高速化するだろうが、軽量化には寄与しないし、アクセシビリティが下がるので実施していない。 トップページで51.5KB、低速なうちの環境でTTFBは14.08ms、DNS lookupとfaviconを除いた時間は101.36msとなった。 従来が500kB/5s程度だったことを考えると、この計測では1/10の容量、50倍の高速化となっている。

MarkdownとReST

今回、Pandocの採用によってMarkdown以外のソースフォーマットに対応することが可能になった。 そして、最初に採用したのがReStructured Text(ReST)である。

Pandocがサポートする中で最もMarkdownに近い汎用性があるのはReSTである。 他にそのようなフォーマットはないが、あるいは専門分野での記述用にDocBookなどをサポートする可能性はある。

なお、入力ソースとしてのHTMLは、判別しがたさの観点からサポートは行わない。

ReSTはMarkdownと比べて表現力に優れている。 記述の簡便性も「好みの問題」と言えるような差異だ。 ただし、ReSTが優れている点について必ずしもそのまま適用できるわけではない。 なぜならばPandocがReSTをフルサポートしていないからだ。

実際のところ他フォーマットへの変換を想定しないのであれば、HTMLを直接記述できるMarkdownの優位性は揺らがない。


  1. 今回はNginxを使ったが、基本的にNginxも静的HTMLファイル向けのサーバーで、アプリケーションは他のサーバーに接続するリバースプロキシとして動作する方式だ。Thinなどを使う手もあるかもしれないが、Nginxよりも高速化するかは疑問。

  2. 800pxのブロックが中央にフロートしているデザインは2005年ころから流行したブログデザインと類似のものだ。今でもamebloなんかはこのデザインなのだが、新規にサイト立ち上げると画面めいっぱいまで使うデザインがデフォルトになっている。

  3. 最速を目指すのであればコードゴルフやminified、画像の排除や機能の統一化なども考えられる。実際のところ「速度は上がるがアクセシビリティが下がる」方式は取ってないし、コスト過剰にもならないようにしている。

新 Aki SI&Eサイト構築と、納得できないCSS解釈

作り直してるよという話はしていたが、ついに全面刷新となったAki SI&Eの作成に着手した。

昨日1日でlightboxプログラムを作り、さらにメニューボタンアニメーションを作った。

Lightboxアプリケーションは作った時点で1kBを切っており、相当軽量な内容になっていた。
JQueryなどは使っていない。

(function() {
  if (! document.addEventListener ) { return false; }
  
  var wrapper = document.getElementById("WrapWindow") /* ModalWindow */
  var fadingTimer = false /* IntervalTimer */
  var alpha = 0.0 /* ModalWindows's alpha number */

  /* draw content */
  var displayContent = function() {
  }
  
  /* fading out (interval callback) */
  var fadeout = function() {
    if (alpha < 0.8) {
      alpha = alpha + 0.05
      wrapper.style.backgroundColor = "rgba(0,0,0," + alpha + ")"
    } else {
      clearInterval(fadingTimer)
      fadingTimer = false
      displayContent()
    }
  }
  
  /* set this for event callback */
  var setLightboxTrigger = function(e) {
    wrapper.style.visibility = "visible"
    fadingTimer = setInterval(fadeout, 30)
  }

  /* Return from lightbox */
  wrapper.addEventListener("click", function(e) {
    if (fadingTimer) {
      clearInterval(fadingTimer)
    }
    wrapper.style.backgroundColor = "transparent"
    wrapper.style.visibility = "hidden"
    alpha = 0.0
  }, false)

  
  document.getElementById("SideNotes").addEventListener("click", setLightboxTrigger, false)


  
})()

基本的な作りはごく単純だ。
htmlbodyheight: 100%;を持っていて、

<section id="WrapWindow">
</section>

#WrapWindow {
  visibility: hidden;
  z-index: 10000;
  background-color: #000; /* for RGBa Unsupported browseres */
  background-color: rgba(0,0,0,0);
  min-height: 100%;
  min-width: 100%;
  position: fixed;
  top: 0px;
  bottom: 0px;
}

となっている。これにより見えていないし触れることもできないが見えている画面より手前に全体を覆うブロックがあり、これがvisibleになることで全体を覆って操作不能にする。

プログラム全体を関数で書こうことで、全体でアクセスできるプロパティを外側に伝播しないように閉じ込めることができる。「クロージャを作ることで変数をローカル化し直ちに呼び出す」というテクニックは、オライリーのJavaScriptで紹介されている。

グローバルな値としてfadingTimeralphaが定義されている。
fadingTimersetInterval()オブジェクトを格納するためのものだ。これをどこかにとっておかないとclearInterval()することができずに困ってしまう。

イベントが発生した時にfadingTimerが真か偽か、によって判断を変えることができる。
fadingTimerが真の状態で呼ばれる可能性があるのは、#WrapWindowに対するクリックイベントだけだ。
なぜならば、fadingTimerがセットされる前に透明なだけでvisibilityはvisibleになっているので、クリックイベントは必ず#WrapWindowが取る。もちろん、なんらかのブラウザの不備でクリックされる可能性はないではないが。
では、「これから真になる、まだ偽の状態で発生するか」というと、真になる関数が走った時点で、JavaScriptはシングルスレッドなので、真になるまで他のイベントを発生させても実行されないので関係ない。
操作に対するフリーズタイムを短くするためにsetTimeout()を使うこともあるくらいだ。

文字列(evalされる)の登録でなくコールバック関数の登録をするためには、intervalで呼ばれるコールバックで繰り返し使われる値を覚えておくことができないので、これはコールバック関数に対してグローバルでなくてはいけない。
そのための値がalphaだ。

1.0になれば終了なのでその時点でclearIntervalだが、まだインターバルタイマーが動作している状態で#WrapWindowがクリックされる可能性はある。これが真の場合だ。

では、その場合どうするかというと、インターバルタイマーの状態に関わらずオーバーレイを消す。そのため、インターバルタイマーが働いていればclearIntervalしてしまう。

なお、最後に追加しているイベントは本番用ではなくテスト用だ。

CSS

今回の目玉のひとつが、main, article, section, header, footer, asideといった「HTML5への対応」だ。
私のサイトにある大量のdivを少しでも減らしたいからなのだが、HTML5への対応は様々なメリットがある。
一方でレガシー環境を切り捨てることになりそうだったので慎重に対応してきたが、NN 4.6相当でも動作しそうな見通しがたったので採用となった。

それはともかく、納得できない振る舞いに随分悩まされた。

        <div id="SideContent">
      	<section>
             <nav id="Toc" class="toc marginbox_main contentbox">
               <h1>目次</h1>
               <ul>
                 <li>ページナビゲーション</li>
               </ul>
             </nav>
        ...
        </section>
        </div>

div, section, nav、あるいはそれらが内包する全てのテキストに対してfont-sizeの相対的変更(80%など)をすると、ボックスが上下とも縮まり、左カラムと位置が揃わなくなる。
インスペクタで見ると、table-cell要素の#SideContentの位置は正しいが、sectionのマージン上辺と#SideContentの上辺の間に隙間ができる。

また、中に入っているボックスの上マージンを削ると、ボックスの高さがそれだけ減ってその分上に隙間が空く。
配置は関係ないらしく、またこれはpaddingが設定されている場合のみ発生する。
paddingを設定したボックスに内包されるブロック要素のmarginをいじるのは、高さを意識する必要がある場合は厳禁ということか。

        <section id="MainArticle">
          <article class="marginbox_main contentbox">
            <h1>記事のタイトル=章</h1>
                ...
          </article>

このh1のマージンを設定するとその分articleが下がる。
h1より上にボックスを置くとボックスは伸びるが、その分右カラムが下げられてしまう。

恐らくは「h1を特別扱いしている」のだろうと思う。
だが、このためにh1を修飾することが非常に難しい。

結局position: relativeにしてずらした。上以外のmarginについてはこうした問題はない。

Markdownで書かれたスライドをHTMLとポータブルPDFと印刷用PDFにする

Aki SI&EのPR用に書いたものだが、Markdownで書き、Web(HTML)、配布用PDF、印刷用PDFの3種類を生成する考えでいた。

手でやろうかと思っていたのだが、再生成の回数も多そうだったので、自動化できるようにした。

Markdownで記述したドキュメントと、オリジナルサイズの画像ディレクトリ、mogrify -geometry x300でリサイズした画像ディレクトリが用意されている。
Web用のものについてはHTMLで出力し、リサイズされた画像のWebサーバー上のコピーを参照しなければならない。

PDFについては、MarkdownをLaTeXに変換してという方法もあるにはあったが、画像を含めた細かなデザインを施したかったため、HTMLをベースにPDFに変換することにした。

MarkdownコンバータはRubyのKramdownを使用。改ページはないため、wkhtmltopdfを用いてPDFに変換することとした。
参照しているディレクトリの違いで2回ループを回す。

それぞれHTMLは別に出力し、テンポラリファイルとして出力したHTMLを元にPDFのテンポラリファイルを生成、unitepdfを使ってこれを結合する。

専用のコードなので多くをハードコーディングしているが、これで正常に機能する。

#!/usr/bin/ruby
# -*- mode: ruby; coding: UTF-8 -*-

require 'kramdown'
require 'erb'

def treat_md(file, findex)
  $article_body = doc = Kramdown::Document.new(File.read(file)).to_html
  
  $article_title = doc[/<h1[^>]*>(.*?)<\/h1>/, 1]
  
  foot = Array.new
  if findex > 0
    foot << '<a href="' + ( SOURCEFILES[findex - 1].sub(/\.md$/, '')  + ".html" ) + '">前へ</a>'
  end
  
  foot << '<a href="/si/" target="_top">Aki SI&Eトップページへ</a>'
  
  if findex < ( SOURCEFILES.length - 1 )
    foot << '<a href="' + ( SOURCEFILES[findex + 1].sub(/\.md$/, '')  + ".html" ) + '">次へ</a>'
  end
  
  $article_footer = foot.join
end


TEMPLATE = <<'END'
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" http://www.w3.org/TR/xhtml1/dtd/xhtml1-transitional.dtd">
<html>
  <head>
    <title><%= $article_title %></title>
    <meta http-equiv="content-language" content="ja" />
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
    <style>
body {
% case $mode
% when :Web
  font-size: 130%;
  color: #333;
  font-weight: bold;
  margin: 0px;
  padding: 0px;
% else
  font-size: 18pt;
  color: #000;
  font-weight: bold;
  margin: 2cm auto;
  padding: auto;
% end
  background-color: #fff;
  font-family: "Migu 1C"
}   

h3 {
  font-size: 135%;
}

#Container {
  margin: auto auto 100px;
  max-width: 950px;
  text-align: center;
}

img {
% case $mode
% when :Web
  height: 300px;
% else
  height: 18em;
  max-width: 100%;
% end
}

ul {
  text-align: left;
  margin: auto 1.8em;
}


#ArticleFooter {
% if $mode != :Web
  display:none;
% end
  position: fixed;
  bottom: 0px;
  margin: 0px;
  width: 100%;
  max-height: 100px;
  background-color: #339;
  color: #fff;
  text-align: center;
}

#ArticleFooter a {
  padding: 0 0.8em;
  text-decoration: none;
  color: #fff
}

#ArticleFooter a:hover {
  text-decoration: underline;
}
    </style>
  </head>
  <body>
    <div id="Container">
    <%= $article_body.gsub(
      %r{src="img/},
      %(src="#{$img_type}_img/)
    ) 
    %>
  </div>
    <div id="ArticleFooter">
      <%= $article_footer %>
    </div>
  </body>
</html>
END

$article_body = nil
$article_footer = nil
$article_title = nil

SOURCEFILES = Dir.entries(".").select {|i| i =~ /\.md$/ }.sort

# HTML
$img_type = "/img/si/intro_slideshow/resized"
$mode = :Web

SOURCEFILES.each_with_index do |x, i|
  begin
  treat_md(x, i)
  File.open("out/#{x.sub(".md", ".html")}", "w") do |f|
    f.puts(ERB.new(TEMPLATE, nil, "%<").result)
  end
  ensure
    $article_body = nil
    $article_footer = nil
    $article_title = nil
  end
end

# Web PDF
$img_type = "resized"
$mode = :PDF

SOURCEFILES.each_with_index do |x, i|
  begin
  treat_md(x, i)
  fn = "out/_tmp_#{x.sub(".md", ".html")}"
    File.open(fn, "w") do |f|
      f.puts(ERB.new(TEMPLATE, nil, "%<").result)
    end
  system('wkhtmltopdf "%s" "%s"' % [ fn, fn.gsub(".html", ".pdf") ])
  ensure
    $article_body = nil
    $article_footer = nil
    $article_title = nil
  end
end

system 'pdfunite out/_tmp_*.pdf out/portable.pdf'
system 'rm out/_tmp_*'

# Printable
$img_type = "original"
$mode = :PDF

SOURCEFILES.each_with_index do |x, i|
  begin
  treat_md(x, i)
  fn = "out/_tmp_#{x.sub(".md", ".html")}"
    File.open(fn, "w") do |f|
      f.puts(ERB.new(TEMPLATE, nil, "%<").result)
    end
  system('wkhtmltopdf "%s" "%s"' % [ fn, fn.gsub(".html", ".pdf") ])
  ensure
    $article_body = nil
    $article_footer = nil
    $article_title = nil
  end
end

system 'pdfunite out/_tmp_*.pdf out/printable.pdf'
system 'rm out/_tmp_*'

完成したものについては

Google DOCs Viewerはスライドショー向きではないので、ウェブ版を使うか、PDFをダウンロードするのがお勧め。

SIサイトの細かなモバイル対応

概要

特にサイトのデザインをいじる気はなかったのだが、 写真を使ったページを縮小した時に画像がはみだしてしまう、という問題があり、 モバイルフレンドリーの観点から修正を実行した。

だが、実際はかなり根が深かった。 結局、PureDocの改修を含む大掛かりな作業になってしまったのだ。

配置方法

私のサイトは見た目はシンプルだが、実際は結構複雑なことをしている。 特にSIサイトに関しては、かなり難しいことを実験している面がある。

SIサイトの難しさは、やはり「レスポンシブデザインで右カラムが上にくる」ということにある。

CSSで指定する場合、左右に並べるのは必ずしも左が先でなくても配置できるが、これを上下にする場合は基本的には「先にくるものが上」になる。 そのため、SIサイトではサイドバーである右カラムがHTML上は「先に」書かれている。

だが、左カラムが先にこない、ということは、左右に並べるために

display: table-cell

が使えないということを意味する。

となると使用できる方法はかなり限られてくるのだが、 しかも高さが一定でない上に、「チェックのボーダー」は親要素の背景になっているため、absolute配置を利用して並べてしまうと、要素の高さが維持されない。 また、両方とも高さが固定でない以上、ネガティブマージンを使う方法も取れない。 floatを使う方法が、事実上唯一だと思われた。

実際に、この問題は試行錯誤の末、既にfloatを使う方法を用いている。 だが、それでもうまくいかない部分があった。

widthとpaddingとmarginの関係

基本的にはwidthをまず確保し、そこにpaddingとborder-widthとmarginを足す。 全体幅を指定したい要素に関しては、それらを0にした入れ子要素を用意するのが無難だ。

しかし、100%の幅を用意したい場合、width: 100%を指定すると、必然的に親ボックスからはみだすことになる。 なぜならば、この時点でコンテンツの幅は親ボックスいっぱいまで確保されていて、その外に足すことになるからだ。

ところが、paddingに相対値を指定すると、paddingはwidthに含まれるのが普通。

つまり、padding: 1em;とした時は、ボックスの外に1emを足すことはしない。 paddingのボックス相対(主に%)を使用する場合、親ボックスはその要素のようだ。 つまり、paddingに25%を指定すると、ボックスの内側50%にコンテンツボックスが生成される。

だが、paddingは相対値は普通にあるが、marginはレイアウトボックスではあまりない。 border-widthに至ってはまずない。

そのために、widthの相対値がとても使いにくい。 UIデザインはスケーラブルな時代になってきているはずなのだが、ウェブの場合、結局スケーラブルな値ではなく、 スタティックなピクセル値を自分で計算して書いて、それに対してスケールするという美しくないことになっている。 しかも、その中にピクセル値に対して曖昧な物理絶対値(特にフォントのポイント指定)を含むのでぐっちゃぐっちゃだ。

結局は全てスタティックなピクセル値に書きなおした。

  • #MainContainerはW850+PAD50+MGNautoで950pxボックス
  • #TopPanはwidth838+border6の850pxボックス。HTMLに直接指定されていたPADは除去。
  • サイズ調整は入れ子のh1要素による指定に変更
  • #LeftPageは590pxでPADauto, MGNは0
  • #SideBarは225pxで左に35pxのMGN。PADは0で、#LeftPageと合わせて850pxボックス
  • つまりカラムの間のボーダーは、サイドバーのMGNが持っている

なぜ画像のmax-widthが効かないのか

厄介なのが画像だ。 画像は既にwidthやheightのボックス相対指定でスケールするようになっている。 一般的にはmax-width: 100%を用いるのだろう。

だが、そもそもの発端はmax-width: 100%が効かない、ということだった。

なぜ効かないのか。実はGeckoでは親要素がピクセル値を持っていなければ画像のwidth/height系プロパティを無視するのだ。 例えば親要素がボックス相対値であっても、その親要素はスタティックなピクセル値ならば問題ない。 例えば幅600pxのボックスに入れ子にされた幅100%のボックスの中であるならば、 画像は600pxに対する幅になる。これは、max-*のプロパティでも効く。

だが、親ボックスがピクセルで固定されたボックスに入れ子にされていても、max-*プロパティで決定されていると効かない。

これがBlinkだと問題ない。例え相対widthの中のmax-widthで決定されたボックスの中であれ、max-width: 100%;を指定すればちゃんと効く。 モバイルでGeckoを使っている人は恐らく少数派なので、あまり問題ない、とも考えられる。

だが、単純なテストを行うと、そのような問題は出ない。 例えば

<html>
<head></head>
<body>
<div style="width: 50%">
<p>
<img src="file:///home/aki/share/pic/vlcsnap-2015-05-12-02h05m49s780.png" style="max-width: 100%;" />
</p>
</div>
</body>
</html>

のようなだ。そこで色々試したのだが、

  • inline-blockの中では画像のwidthは効かない。これはGeckoでは効かないがBlinkでは効く
  • block要素の中ではfloatされている要素は高さを持たないが、inline-blockの中では持つ。これはGecko/Blink共通

ということが分かった。

多分だが、これは明確な仕様にはなっていない。曖昧な部分なのだろう。 仕様に準拠した「論理的な記述」からはかけ離れているが、実際の挙動に合わせて意図した通り動かすため、 #UnderContainerを950pxを超える場合はinline-blockに、950px以下ではblockになるようにした。

その他の修正

Webfontの修正

SIではなくReasonSetのほうで使用しているwebフォントのロゴたいぷゴシックだが、OTFのほうがグリフはかなり多いのだが、かなり空白グリフがある。

空白グリフというのは結構な問題だ。というのは、通常、グリフがない文字についてはフォールバックされ、他の代替フォントを使用して表示する。 そのため「表示されないから読めない」ということは避けられるのだが、空のグリフが存在していると結局空白を表示してしまうため、文字が表示されないため見えない。

空のグリフは削除してしまうのが望ましい。 これを問題にしたのは、「精悍」の「悍」の字が出なかったためだ。

これをcockscombさんのGitHubコード を使用して修正した上で再生成してこの問題を修正した。

PureDocの修正

PureDocのPuredoc::XHTPureDoc.stdメタメソッドがオプショナルなパラメータを正しく解釈しなかったため、修正した。

これはPuredoc::XHTPureDoc.getoptionsメソッドが戻り値をちゃんと指定していなかったためである。

原稿のGitリポジトリ

原稿はCodebreakで草稿も含めて公開しているが、 久しぶりに更新した。若干歴史にも改変を加えたが、ゴミの除去が主だ。

プロフィール

プロフィールに部活の履歴を追加し、 項目としてBiographyを加えた。

サイトの大幅手直し

ウェブサイトに直すべき点が大量にあったためにかなり手をいれることになった。

CSSグラデーション、デザインの修正

まずベンダープレフィクスを用いたCSS3のグラデーションを入れた。

/* headline level2 decoration */
h2 {
width: 100%;
background: -moz-linear-gradient(left -65deg, #fff, #acf);
background: -webkit-gradient(linear, left top, right bottom, from(#fff), to(#acf));
}

これはAki SI&Eのほうのもの。これを使い、サイトロゴは透過画像として(新規に作成した)、背景にグラデーションを入れた。画像はフォアグラウンドのイメージ

/* Header container (Top position of Main container) */
#Header {
background: #fff;
padding: 0 1.3em;
max-height: 125px;
}

#SITE_REASONSET #Header img.sitebanner {
margin-right : 40%;
width: 50%;
}

#SITE_REASONSET #Header {
background: -moz-linear-gradient(left -65deg, #f0f8ff, #acf);
background: -webkit-gradient(linear, left top, right bottom, from(#f0f8ff), to(#acf));
}

SI&Eのほうはメインコンテナが950pxとっているのに切り替えを750pxにしていたのでこれも修正。

Aki SI&Eのほうはキャプチャのスタイルを変更。

h1 {
border-top : double 5px;
border-bottom: double 5px;
color: #39f;
margin-left: -1em;
margin-right: -1em;
margin-top: -0.8em;
text-align: center;

}

マイナスのmarginはあまり見ない気がする。このようなデザインはほとんど見ることはないが、なかなかパッとみてすっきりと見えて目を引く良いデザインだと思う。このために、テキストインデントのマイナスは消した。テキストインデントのマイナスは一行目にのみ適用されるらしい(brで改行すればその都度適用されるのだろうが、折り返しには適用されない)。

サイトバーウィジットの変更

AKi SI&Eが先行で変更され、それがReasonSetに反映され、さらにReasonSetで追加された機能がAki SI&Eのほうに入った。

<!-- Sub column -->

<!-- Menu -->

-*- Menu -*-

<%= ENV["SUBCOLUMN_CONTENT"] || "" %>

<!-- Notes if any -->
% unless DOC.notes.empty?

-*- Notes -*-
    % DOC.notes.each do |note|

  1. <%= note %>
  2. %end

% end

<!-- Navigation links with PureDoc and PureBuilder template -->
% if DOC.meta["link"] && ( DOC.meta["link"]["next"] || DOC.meta["link"]["content"] || DOC.meta["link"]["prev"] || DOC.meta["link"]["start"] )|| ENV["reasonset_link_content"] || ENV["reasonset_link_start"]

-*- Referrances -*-

% end

<!-- / Sub column -->

このために、これまでSubCloumnコンテナ直下にメニューがあったが、それとcontent_boxクラスを分離して、同クラスのdiv要素を追加している。

/* White background */
.content_box {
background-color: #fff;
}

/* Padding for continuous content box. */
.content_box + .content_box {
margin-top: 11px;
}

Aki SI&Eのほうはもう少し手の込んだものになっている。

#SideBar .container {
padding : 1.28em;
}

#SideBar .container:last-child {
margin: 0;
}

CSS3の:last-child擬似プロパティを使うことで最後が間延びしてしまうことを防ぎながら、要素を分離してチェックをみせるようにしている。

注釈をサイドバー内にも表示するようにした他、内容的にはこれまでコンテンツ側にあったACCSのserial articleのreferranceをサイドバーに表示するように変更した。

レイアウト方法の変更

これでサイドバーが長くなったのだが、なぜかこれではメインコンテナが短縮されてしまう。あくまでメインカラムの高さに合わせるのだ。構造としては

なのでこの挙動は謎だが、MozillaでもBlinkでも同様の挙動を示す。

floatすると一見うまくいくようだが、逆にサイドカラムの高さに合わせてしまい、メインカラムが突き抜けてしまう。また、幅もきちんと規定しなくてはいけない。

結局、3者全てinline-blockにしてしまえば高さは正しく確保される。ただし、これでは2つのカラムはmarginがとられないためぴったりくっついてしまう。

そこでこれをmargin, padding, border-widthが全て0のコンテナとし、paddingは別のブロックで確保する。paddingは設定してもよかったのかもしれないが、このほうが管理しやすい。

エッセイ用のほうが先行して開発されたので、構造が甘かった。ReasonSetはもう少し安定しているが、やはり作りなおしたほうが良いと思われる。ちなみに、ReasonSetが安定しているのは、高さが規定されているためでもある。

ここまでしたのだが、inline-blockで幅を%指定だとGeckoでは見られるが、Blinkでは上下にレイアウトされてしまう。ピクセル指定にすれば大丈夫だが、relativeにした上でレイアウトし、さらにfloatすれば%指定でもBlinkで正常に表示される。

ACCSの修正

まず、TOC機能がエラーになるようになっていた。以前はRuby2.0でやっていたが、2.1になったからだろうか?どうも、クラスが違うオブジェクトを持つArrayを比較しようとしている、ということらしい。文字列しか格納しないように思うのだが、一応to_s

essaies.push [ sortitem.to_s, fp.to_s, meta["title"] ]

TOCに戻るリンクが間違っていた。これは、設定ファイルがreasonset_link_contentsとしているのに、テンプレートはreasonset_link_contentとしているという単純な理由だった。これは、HTMLのlink要素に対応するのでcontentsが正しい。ちなみに、Essayにはその機能がそもそも含まれていなかった。

そして、ACCS記事間のリンクが機能しない。Talkin' About(Aki's Palace)との違いが見つからず、一体…と思ったのだが、grepしてみるとその設定はrbutilに含まれており、chienomiが専用で使っている。chienomiのリンクが機能していたので、当然Talkin' Aboutでも機能しているものと思い込んでいたが、実際はTalkin' Aboutでは機能しないわけだ。単に記事がひとつしかないので気づかなかっただけだ。

基本的にはACCS記事内でそのスクリプトをロードすれば機能するのだが、ロードすべきスクリプトが存在しない。chienomiのものはパスがハードコーディングされている。そのため、それを修正したバージョンを用意した。

また、「最初の記事」だが、

export reasonset_link_start="si/$wd_element/${artdir:+${$(print $artdir/*.pdoc):r}.html}"

となっていた。Talkin' Aboutでは機能していたが、これは記事がひとつしかないからだ。つまり、複数のファイルがあると、それが連なった文字列(スペース区切り)の最後だけpdocをhtmlに変えることになってしまう。

当然、先頭のファイル名のみをとり出さなくてはいけない。(#q[1])で機能しなかったので、

export reasonset_link_start="si/$wd_element/${artdir:+${$(print -l $artdir/*.pdoc | head -n 1):r}.html}"

と変更した。単純な方法だが、そこまで繰り返し呼び出すわけではないので、head(1)を呼び出す程度どうということはないだろう。

ノートインデックスの作成

ノートのインデックスを作るため、まずPureDoc側を修正。その機能をスーパークラスに追加。

### Notes ###
# Add note text to an array
def addnote(note)
@notes.push note
end

attr :notes

サブクラスで呼び出し

# Note
def n(text, ¬e)
puredoc_element(:n) do
note = note.call
addnote note
'%s</pure:note>' % [esc!(note), esc(text) ]
end
end

ちなみに、HTMLクラスタイプだけノートとテキストが逆になっていた。どちらが正しいのかよくわからなくなっている。仕様を規定すべきだろう。

そして今修正した。

def n(note, &text)
puredoc_element(:n) do
addnote esc!(note)
'%s</pure:note>' % [esc!(note), esc(text.call) ]
end
end

これでノートインデックスの作成が可能になった。だが、これに対応した表示が本文に必要だ。これはCSSに任せる。

body {
counter-reset : notes;
}

/* Note element */
.puredoc_note:after /*, puredoc|note:after */ {
vertical-align: super;
font-size: 80%;
content : "\002020" counter(notes);
counter-increment: notes;
text-decoration: none;
}

content中の文字参照はどうするのだろう、と思ったら、16進数にした上でクォート内で

HTML * CSS 美しいフレームをもつカラムスタイルレイアウトの難しさ

私のサイトをみるとわかるのだが、ボーダーが画像になっている。これをどうやって作るのか、というとごくごく単純で、背景画像を持つdiv要素に入れ子で白背景のdiv要素をおいているだけだ。ちなみに、外側のdiv要素にpaddingを設定するのが正しいやり方だ。

ところが、これで左右カラムを置き、しかもその中にブロック要素をおこうとするとかなり難しい。floatを使って配置すると、内側のブロック要素は親要素のブロックを突き抜けてくっついてしまうのだ。普通なら左右カラムはくっついて配置されて構わないものだから、このような挙動は気にしないのだが、今回の場合、左右カラムの間に空間を作ってボーダーを表示したいため、工夫がいる。

とりあえず、まずは縦に並ぶブロックを分けろ、だ。左右カラムはブロックの中に入れて左右分割すべきだ。でないととても苦労する。

さて、問題の比較的単純な解決策はfloatを使い、左右それぞれにfloatし、かつ重ならないようにwidthを設定する方法だ。だが、私はfloatを使う方法があまり好きでない。なんらかの理由で重なってしまうと後に書いたブロックは後ろにいってしまうという、表示への影響が大きいからだ。

そのため、私がとった方法は

  1. static配置では中のブロックに対してabsoluteが使えないので外のコンテナはrelativeで配置
  2. 右に配置されるサイドバーコンテナを先に書く
  3. サイドバーコンテナはabosluteで配置。widthz-indexを設定
  4. メイン部分コンテナはrelativeで配置。同様にwidthz-indexを設定
  5. サイドバーコンテナ、メイン部分コンテナ共に内側に入れ子のコンテナを配置(白背景)
  6. 外のコンテナのwidthpaddingでボーダーを適切にする。つまりギリギリにしないほうが良い
  7. サイドバーコンテナにmax-height、メイン部分コンテナにmin-heightを設定。サイドバーコンテナはoverflow: auto

簡単な話なのに複雑、と感じただろうか。実はそう簡単ではない。確実に正確な位置に配置しようとすると、floatしないならabsoluteで置くしかない。ところが、そうしようとするとstaticのまま親コンテナが配置されていてはいけない。

サイドバーコンテナを先にするのも理由はあるが、別に好みだとは言える。

widthの設定はもちろんかさならないように、またボーダーのサイズを調整するためで、z-indexは万一重なった時のためだ。

入れ子にするのは白背景とボーダー調整のためだが、この方法ではなくてもできる。

さて、なぜ両方absoluteにしないかというと、そうするとこのブロックは高さ0であるとみなされ、下側のボーダーが出ない(下にフッターを配置した場合、それも出ない)。そのため無限に長くなるメインのほうをrelativeにしているのだが、そうすると本文よりもサイドバーが長くなるときに支障が出る。そこで、サイドバーの最大長を制限すると共に、本文の最低長を要求することで、サイドバーのほうが長くならないようにしている。

別に「すごく難しい」という話ではないのだが、これはなかなか出てこない話だ。というのは、かなり技術的なことをしているが、それは構造的、意味的なものではなく、完全にデザインのための記述だ。デザインのための技法というのは技術者にはなかなか身につかない。そして、デザイナーがデザインを実現するための技術もそうそう思いつかない。ボーダーに画像を使いたいと言われた時、エンジニアがそのような機能はないのでできない、と言ってしまう可能性もある。今回やっていることはどちらかというとデザイナー主体のことで、デザイナーがそれを実現するための技術的知識があるか?というところが問われる。

このようなデザインは非常に個人サイト的で、商業サイトではトレンドもあるためこのようなデザインを採用することはない。というよりも今は非常にDHTML流行りなので、そもそもアクセシビリティ自体がナンセンスのように言われることも少なくないし、こうした指向がないために見かけないのかもしれないが、アクセシビリティとデザインを両立し、独自性があり、かつ習慣に基づくなじみやすさを満たす、というこうした技法は、そうしたサイトをみないだけに抜け落ちるポイントになっているのではないか?と思う。

今回は他の方法もあるにはあるのだが、これが最もアクセシビリティが高い方法だと思う。個人的には、最新のウェブブラウザでなければ見られないようなサイトは好きでない。

なお、現状公開しているサイトはこの仕様になっていない。現在テンプレートを修正したところだからだ。恐らく、本体ウェブサイトではなく、商業ページのほうが先にこのレイアウトを採用するだろう。現在の本体ウェブサイトは類似のデザインを採用しているが、バグがある。


/* Skin CSS for general essay.
* This is used by essays independent from the site.
*/

/* Top definition */
body {
background: #fff url("//reasonset.net/img/wp/heartline.gif") repeat-y 15% 0% scroll;
font-size: 10pt;
color: #39f;

}


/* Main Container. Over all container. */
#MainContainer {
width : 950;
margin : 0 auto;
background: #e0f0ff url("//reasonset.net/img/wp/check_bp.png") repeat scroll;
font-family: "Rounded-X M+ 2c","Migu 1C","M+IPA+2VM circle","Gen Jyuu Gothic Normal","VL P Gothic", "Hiragino Maru Gothic", "Meiryo",sans-serif; /* define Japanese Font */
position: relative;
padding: 45px;
}

/* For overwrite font-family for English document. */
#MainContainer {

font-family: serif;

}

/* Top banner */
#TopBan {
margin : 0px 0px 18px 0px;
font-size: 300%;
padding: 1.3em;
position: relative;
}

#UnderContainer {
position: relative;
}

/* Main content container */
#LeftPage {
position: relative;
top: 0px;
left: 0px;
z-index: 1000;
/*   margin: 30px; */
padding: 0;
background-color: transparent;
/*   border: blue 1px solid; */
width: 650px;
}

#SideBar {
padding: 0;
/*   border: blue 1px solid; */
position: absolute;
right: 0px;
top: 0px;
width: 250px;
z-index: 200;
}

/* Any container. */
.container {
background-color: #fff;
padding : 4em;
/*   border: red 1px solid; */
margin: 0;

}

/* paragraph */
p:first-letter {
text-indent: 1em;
}

p {
line-height: 1.2;
letter-spacing: 0.25em;
}

/* headline to left */
h1,h2,h3,h4,h5,h6 { text-indent: -1.3em; }



<html>
<head>
<link rel="stylesheet" type="text/css" href="./puredoc.css" />
<link rel="stylesheet" type="text/css" href="./puredoc_skin.css" />
<link rel="stylesheet" type="text/css" href="./gen-essay-skin.css" />

</head>
<body>
<div id="MainContainer">

<div id="TopBan" class="container">
Site title.
</div>
<div id="UnderContainer">

<div id="SideBar">
<div class="container">Sometext</div>
</div>
<div id="LeftPage">
<div class="container">
<h1>H1 title</h1>
<h2>h2 title</h2>
<p> p1</p>
<p>p2</p>
</div>

</div>
</div>
</div>



</body>
</html>