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

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ではフォントインストールにセキュリティリスクがあるという警告が出されていた。

ConoHaへのサーバー引っ越しレジュメ (DeleGate-Nginx/Postfix/Dovecot, Let’s Encrypt)

概要

作業概要は以下の通りだ

  • サーバーをDTI(@ServerMan)からConoHaに変更する
  • サーバーOSをCentOS (6.9)からArch Linuxに変更する
  • WebサーバーをDeleGateからNginxに変更する
  • WebコンテンツシステムをPureBuilder2からPureBuilder Simplyに変更する
  • Mimir YokohamaのWebコンテンツを別サーバーのWordPressから新サーバーのPureBuilder Simplyに変更する
  • Webメニューを完全JavaScriptフリーにする
  • Postfix/Dovecotを引っ越しする
  • DNSサーバーをConoHaとし、DNSの設定を変更する
  • 一部のドメインを廃止とする
  • 一部のドメインの役割を変更する
  • Let’s EncryptによるSSL証明書を取得し、HTTPSに対応する
  • 同証明書にメールも対応させる

なお、あまり詳細な解説はしていない。 Linuxやサーバーに関する基本的な事項に対して理解していないまま実施するのは危険であるため、 「コピペ仕様」にはしていない。 詳細に対してエラーが生じるなどの理由でより情報が欲しい場合はあるだろうが、 「言っていることが理解できない」のであれば、サーバー構築をするにはまだ危険なレベルにあると思ったほうが良いだろう。

承前: 開発

PureBuilder Simplyの開発

今回のために新しいコンテンツシステムであるPureBuilder Simplyを開発した。 この詳細は別に譲ろう。

テンプレートの開発

PureBuilder SimplyはPandocテンプレート+eRubyテンプレートというテンプレートを扱うことができる。 PureBuidler Simplyにテーマファイルがあるわけではないので自分で書く前提である。

今回は複数の表示デザインがあるのだが(現在のところは表示されるのはアーティクルモードとプロモーションモードの2つだけだが)これは全てPandocテンプレートとCSSで実現している。

もちろん、このようなテンプレートの開発はWeb屋の腕の見せ所だろう。

CSS

サイトはそれほど華やかなデザインではないが、技術的に劣っているわけではない。 その最たるものがCSSのみで書かれたハンバーガーメニューとドロワーだろう。

ポイントを簡単に言うと

  • ドロワーはfixedで上部に最初からある。display: noneで、高さは比較的新しい単位であるvhである
  • ハンバーガーメニューはtransitionを使っている
  • z-indexでメニューのほうが上になるようにする
  • 状態変遷は次のような方法でコントロールしている
    • 表示されないチェックボックスを用意し、操作対象をlabelで関連づける。これによりlabelで囲まれた要素をクリックするとチェックボックスがトグルする
    • CSSの+セレクタは同じ親要素を持つ次の兄弟要素に適用される
    • チェックボックスにはチェックが入っているときに適用される疑似要素:checkedがある
    • そこで、チェックボックス本体の直後に対象となるセクションコンテンツを置くことでチェック状態によって状態をトグルできる
    • ドロワーはfixedなのでどこに書いても構わないので、このセクションコンテンツの中におく。場所としてはメニューの位置に基づくのが良いだろう

見た目はWordPress時とあまり変わっていないが、性能は大幅に向上した。

速度

キャッシュの効かない初回アクセスで従来のWordPressページが5.57s、新しいページは約400msと10倍程度高速化した。

ConoHaサーバーのスタートアップ

立ち上げ

ConoHaのサインアップは10分もあれば可能で、即座にサーバーを使いはじめられる。

サーバー仕様は東京リージョンの1GBプランである。

512GBプランは安いが、性能が十分でなく、またマイグレーションができないため、1GBプランとしている。

この時点でサーバー選択が可能なので、Arch Linuxを選択する。 また、この時点である程度のセットアップが可能だ。 ここでSSH公開鍵の登録が可能である。

のようにして専用に生成しておく。 そしてconoharoot.rsa.pubを登録する。 名前からもわかるように、ここで登録する鍵はroot鍵である

また、開放するポートも選択できる。 ここで開放するポートはサーバーよりも上流でフィルタリングされる。 ここで透過していないポートに対するアクセスはサーバーに到達しない。

ただし、結果的にSSHポートの変更はサーバーに負担がかかるものになっている。 少なくともパスワードログインは禁止すべきだろう。

現時点ではSSHのみを通過させる。

初期設定

ConoHaのArch Linuxは標準インストールではなく、ある程度調整されている。 主にSSHログインが可能で、SSH公開鍵が登録され、パスワード認証不可となっている、といったことだ。 こうした内容は/etc/cloud/conoha-init/conoha-init.shによって確認可能だ。

複数のサーバーを接続する予定であれば変更すべき点は多いが、そうでなければ作業は平凡だ。

アップデート

まずはアップデートする。 20MB/sも出るため、超早い。

Zsh

私としてはZshがないと話にならないので入れておく。

これ移行はZshでの作業

一般ユーザーの追加

もちろん、一般ユーザーは登録されていない。

この時点で一旦再起動しないとパスワード設定ができないので、再起動しておく。

パスワードの設定

その上でvisudoを使って%wheelに対するsudoを許可する。

pacaurとyaourtのインストール

AURパッケージも扱う予定であるため、pacaurを入れておく。

# su - jrh
% gpg --recv-keys 487EACC08557AD082088DABA1EB2638FF56C0C53
% sudo pacman -S git

%git clone --depth=1 "https://aur.archlinux.org/cower.git"
%cd cower
%makepkg -si
%cd ..

%cower -d pacaur
%cd pacaur
%makepkg -si

これでAURからのパッケージインストールが可能になったので、Yaourtを入れておく。 作業が明確に自動化されていたり、システマチックに行えるのならYaourtはいらないかもしれないが、 まとめて検索するためには欲しい。

% pacaur -S yaourt

必要なソフトのインストール

ここでいう「必要なソフト」は私の取り扱い上の話だ。

% yaourt -S ruby openbsd-netcat rsync mercurial postfix dovecot nginx fcgiwrap certbot certbot-nginx vim

一般ユーザーでの鍵ログインの準備

サーバーで受け入れの準備をする

% mkdir .ssh

ローカルで鍵を生成しておく。

% ssh-keygen -f ~/.ssh/conoha.rsa

~/.ssh/configに設定する。 これは名前によるアクセスを可能にするためである。 (同一ホストに対する異なる鍵でのアクセスのため、鍵を指定せずに済むようにでもある) もちろん、読み替えること;

Host conoha-root
  User root
  Port 22
  HostName conoha.site.static.cnode.io
  IdentityFile ~/.ssh/conoharoot.rsa

Host conoha
  User jrh
  Port 22
  HostName conoha.site.static.cnode.io
  IdentityFile ~/.ssh/conoha.rsa

rootでは作業できるので、転送する。

% ssh conoha-root 'cat > /home/jrh/.ssh/authorized_keys' < ~/.ssh/conoha.rsa.pub

サーバー側でパーミッションの設定を行う

% sudo chown -R jrh:jrh .ssh
% chmod go-a -R .ssh
% chmod go-a .

そしてsshdリロード

% sudo systemctl reload sshd

DNS設定

ドメインを複数持っていない場合はいきなり完全移行できなかったりするので、 一時的にサブドメインを作るなどする必要がある。

ConoHaコントロールパネルのDNSからDNSの設定を行う。 (DNSの設定ってなんだ、という者はサーバーを立てるにはまだ早い。修行してくるべし)

これは本番サーバーのものを含む。 ただし、稼働中の本番サーバーのアドレスをこの時点で変更してはならない

そして、DNSサーバーをConoHaのものに変更する。

今回の場合従来はドメインサービス提供のDNSを使用していたため、同サービスのメニューから変更を行った。

WebとLet’s Encrypt

Nginxの立ち上げとテスト

従来がDeleGateで、Nginxにするため互換性は全くない。

Nginxは単純にstartすればアクセス可能な状態だが、移行のための準備をしていく。

まずは、Archの記述に合致するようにserver-avilableディレクトリを読むようにしたほうが良いだろう。

http {
    include       mime.types;
    include       /etc/nginx/servers-avilable/*;
    ...
}

これで/etc/nginx/server-avilableを作ればそこに配置したファイルを読むようになった。 この時点でrestartすると…

Dec 19 18:45:42 archlinux nginx[9339]: 2017/12/19 18:45:42 [warn] 9339#9339: could not build optimal types_hash, you should increase either types_hash_max_size: 1024 or types_hash_bucket_size: 64; ignoring types_hash_bucket_size

とのことなので増やす。

http {
    include       mime.types;
    include       /etc/nginx/servers-avilable/*;
    default_type  application/octet-stream;

    server_names_hash_bucket_size       128;
    types_hash_max_size                 4092;
    ...
}

配置するファイルはArchwikiに従ったもので大丈夫だ。

server {
  listen 80;
  listen [::]:80;
  server_name domain.tld;
  root /usr/share/nginx/html;
  location / {
    index index.htm index.html;
  }

  # ACME challenge
  location ^~ /.well-known/acme-challenge/ {
    default_type "text/plain";
    root /var/lib/letsencrypt;
  }
}

domain.tldは適切なドメインに置き換えること。

ここで、あまり書かれていない重要なこと。

rootというのはドキュメントルートで、locationの起点となる。 つまり、

location /foo {
  root /usr/share/nginx/html;
}

であるとき、/foo/bar.htmlにアクセスすると読まれるファイルは/usr/share/nginx/html/foo/bar.htmlであって、/usr/share/nginx/html/bar.htmlではない

そのような単純なマッピングを行いたい場合はrootではなくaliasを使う。

ACME challengeはhttp://domain.tld/.well-known/acme-challenge/randomnumberに対して行われるため、 rootで正しい。

だが、このディレクトリがないので作っておいたほうが良いようだった。

% sudo mkdir -p /var/lib/letsencrypt/.well-known/acme-challenge

あとはcertbotを使えば良いのだが…

% sudo certbot certonly --email webmaster@domain.tld --webroot --webroot-path /var/lib/letsencrypt/ -d domain.tld

その前にConoHaのサーバー設定でHTTPをあけておかなくてはならない。 そして、IPv4とIPv6両方をあけておくこと。 別々になっていることに気づかず、ACME Challengeのリクエストが到達せずに(/var/log/nginx/access.logを見ても記録されていない)随分とハマってしまった。

移行作業

各ドメインごとのドキュメントルートを作り、ファイルを配置、 さらに対応したドメインごとの設定ファイルを作る。

今回の場合、Mimir Yokohamaのページは既にPureBuilder Simplyによる静的ファイルへのビルドが完了しているし、 移行対象のものに関してもPureBuilder2で静的ファイルにビルドされているものなので、単純にファイルを配置するだけの簡単なお仕事である。

Aki SIE関連のアドレスはdiscontinuedなので、301を返す。

server {
        listen 80;
        listen [::]:80;
        server_name aki-sie.com akisie.com aki-sie.yokohama akisie.yokohama;

        return 301 http://www.mimir.yokohama/;
}

http://journal.reasonset.net/に関しては301でリダイレクトしていたので、これも反映する

server {
        listen 80;
        listen [::]:80;
        server_name journal.reasonset.net;
        return 301 http://chienomi.reasonset.net$request_uri;
}

NginxのLet’s encryptの対応

メールサーバーの移行まで完了した時点で作業を行うのだが、 certbotで必要なドメインをすべて署名してもらったら、1SSL対応化を行う。

設定例は以下

server {
  listen 80;
  listen [::]:80;
  listen 443 ssl http2;
  listen [::]:433 ssl http2;

  ssl_certificate /etc/letsencrypt/live/domain.tld/fullchain.pem;
  ssl_certificate_key /etc/letsencrypt/live/domain.tld/privkey.pem;
  ssl_trusted_certificate /etc/letsencrypt/live/domain.tld/chain.pem;
  ssl_session_timeout 1d;
  ssl_session_cache shared:SSL:50m;
  ssl_session_tickets off;
  ssl_prefer_server_ciphers on;
  add_header Strict-Transport-Security max-age=15768000;
  ssl_stapling on;
  ssl_stapling_verify on;

  server_name domain.tld;

  index index.html;
  location / {
    root /var/www/domain.tld/doc;
  }

  # ACME challenge
  location ^~ /.well-known/acme-challenge {
    root /var/lib/letsencrypt;
  }
}

メールサーバーの移行

機能させるために一旦古い証明書で作業しているため、Let’s Encryptの証明書が取れたら証明書ファイルを変更してreload/restartすること。

Postfix

Postfixに関してはCentOS 6とArch Linuxではファイル配置が異なり、 バージョンの違いから設定ファイルの違いも大きく互換性に乏しい。

そこで、CnetOSの設定ファイルを/etc/postfix.centとしてコピーする。 このほか、/etc/alias*も忘れずに移行する必要がある。 また、一旦古い証明書ファイルもコピーした。

設定ファイルの違いを見るため、diffを取る。

% diff /etc/postfix.cent/main.cf /etc/postfix/main.cf > main.diff
% diff /etc/postfix.cent/master.cf /etc/postfix/master.cf > master.diff

このdiffファイルをkwriteで2開く。 今回はNemoのsftp機能を使って開いた。

このdiffを元に手作業で変更箇所を反映していく。 主にssl, auth, virtual関連の変更を反映する必要があった。

virtual関連のファイルは/home/mailuserにあったため、これをコピーする。 ただし、これはvirtualメールボックスを含むため、移行完了までは継続的にアップデートする必要がある。 この前にvirtual用に設定していたmailuserを設定する必要もあった。

% sudo useradd --no-create-home --uid=20000 --shell /bin/nologin --system -U -M mailuser

だが、これだけではグループが適正な値にならない。 そこで、グループIDを変更した上で、ユーザーの所属する主グループIDも変更する。

% sudo vigr
% sudo vipw

Postfixをrestartして完了。

Dovecot

Dovecotに関しては設定ファイルをコピーしてそのまま使うことができた。

Dovecotの設定ファイルとして/home/mailを使っていたので、これをコピーする。

Dovecotをrestartして完了。

上流

DNSを変更してもDNSキャッシュの関係でまだ旧サーバーにもメールが届く。 ある程度はaliasを使って転送しても良いのだが、素直にPostfixで受け取りつつ、Maildirのファイルを同期していくのが良いだろう。

DeleGateを使っていたため、DeleGateのSMTP転送機能を使う方法もあるのだが、うまく動作しなかったのと、TLS接続を受け付けることができないため、諦めた。

セキュリティ関連

複雑な話になるので省略。

そこまで難しいことをしているわけではないが、 普通の対策と、らしい対策とで簡単には突破できないようにしている。

後処理(旧サーバー)

DovecotとDeleGateは停止してしまう。

Postfixは5日程度稼働したら停止してしまっていいだろう。3

その後、バックアップ(主にログや設定など)をとったらサーバー破棄である。

おまけ: ConoHaについて

DNS

ConoHaのDNSは高速である上に非常に設定しやすい。

これまで使用していたお名前.comのもの、XDomainのものは非常に設定しづらかったので、 これだけでもConoHaにする価値はあるレベルだ。

料金

コンピューティングリソースはDTIとあまり変わらないが、料金は約倍になった。

ただし、ConoHaの柔軟性やメリットを考えればそう悪くない。 両方持つと少々負担は大きいが4、一気に完全移行したためDTIを解約でき5、許容すべきコストアップだろう。

速度

速い。

「コンピューティングリソースは大きく変わらない」といったが、速度は明らかにあがった。 特にネットワークの高速化とストレージの高速化の恩恵は大きく、Webの応答性は明確に向上した。

柔軟性

DTI VPSは色々と柔軟性が足りず、困った。 特に、OSのバージョンをあげようにもサーバーがひとつしかないので、できない。 ConoHaなら

  • インスタンスが立てられるのでサーバー仕様変更もできる
  • OSが選べる。さらにカスタムイメージのアップロードも可能
  • サーバーのアップグレードが簡単にできる(512MBプランを除く)

ファイアウォール

上流でフィルタリングしてくれるのはとても嬉しい。 設定が楽になる、というのもあるが、サーバーの耐障害性が勝手にあがる上に、まあまあ重いiptablesでの処理量が減るため、パフォーマンスが改善する。 リソースが限られている中では非常に嬉しい。


  1. Let’s Encryptの証明書はSAN(Subject Alternative Name)に 対応したもので、fullchain.pemが全てのドメインに対応したものになる

  2. Kwrite/Kateにはdiffのハイライト機能があるからだ

  3. TTLは3600だったため、それに従うならば1時間待てば良いのだが…

  4. DTIが月額500円ほど(年間6000円程度), ConoHaが900円ほど(年間10800円程度)である

  5. といっても、私はDTI SIMの契約があるため、DTIの契約そのものが終了するわけではない

新 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についてはこうした問題はない。

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>