「HTML」とwebの話

最近、「webエンジニアは閉じた世界しか見ていないのだろうか?」と感じるようなことがいくつもあった。

例えば

  • Ruby on RilasとRubyを混同する
  • JavaScriptとJQueryを混同する
  • 言語処理系と言語を混同する
  • 著名なフレームワークを使うのが当然であり、それ以外は存在しないものと考える
  • 全く異なる分野や利用法で使われている技術だということを頑なに認めない
  • 自分が勤めている会社の構造や手法や評価尺度が社会的絶対基準であると信じる

などだ。

で、今回は「HTMLとwebを同一視し、HTMLをwebだけのものであると考える傲慢さ」についてである。

HTMLの歴史と経緯

HTMLとwebは根本的には同一である、というのは正しい。

多くの人が知るとおり、webはCERNにおいて論文共有・参照手段として登場し、HTMLもまたこのために登場している。

HTMLはHyper Text Markup Languageなわけだが、「Hyper Text」とはなにかといえば、ハイパーメディアの一部たるテキストである。 じゃあハイパーメディアとはなにかといえば、multimediaに関連性をもたせたものである。

この場合、ハイパーメディアといいつつも現実的に考えられるのは画像くらいのものであった。 だが、潜在的には画像や動画を含めることができることを意識していた。

それらを文書でつなぐ、それがHTMLの役割である。 実際、HTMLのアンカーは「ハイパーリンク」と呼ばれている。

ここでは「マルチメディア」というのがテキスト、文書を含むものであることを忘れてはいけない。 これもまた当然に立派なメディウムである。

HTML2の時点では構造化文書であることが非常に強く意識されていた。 と、同時に、これが視覚的メディアであることもまた強く意識されていた。 この時点では論理性というよりも、視覚的意味合いによって構造化されるという感覚が強く、この過程は同じく論文のためのフォーマットとして誕生したTeXへの意識(あるいはroffへの意識)が感じられるものであった。

そう、ここまで構造化文書というのは、「章立て」といった概念はあるにせよ、文書自体が論理的構造を持っている、というのとは少々異なった。 論理的構造と視覚は一体だったのだ。これが当たり前のことであった。

HTML3によってこれは混迷を極めた。「webをリッチにしたい」という欲求はとどまることを知らず、JavaScriptが誕生し、より装飾的な機能が多く追加された。

一方で、HTML3時代には2つの、異なる意味が生まれていた。

ひとつは、web技術の応用だ。 webをあくまでWWWの世界に留めるのではなく、より広く使いたいという考え方があった。 既にWWWはインターネットの主要コンテンツであり、コンピュータはインターネットのためのものになりつつあった。 競争に勝ち抜くにはweb界の覇者となる必要があり、そのために力を注いだ技術を他のところでも使いたいと考えた、という流れだ。 例えばWindows 98のActive desktopなどがそれである。

もうひとつは、文書フォーマットとしてのHTMLの普及だ。 従来ドキュメントフォーマットというのはあまり良いものがなく、恐らく最善なのはPDFであり、そこにいたるまでのTeXであった。 Windowsは(docドキュメントを含め)いくつものドキュメントフォーマットを持っていたが、それらは標準化されておらず、Microsoftに閉じた存在であった。 roffはman roffであっても「イケてない」と考えられていた。 なにより最大の問題は表示向きでなく、HTMLは圧倒的に表示に適しているということだ。

だから、HTMLはヘルプドキュメントをはじめ、幅広く使われるようになった。 単にドキュメントファイルとして使われるだけではなく、様々なところで組み込まれて使われるようになった。

HTML4は決断のフォーマットであった。 HTMLはドキュメントフォーマットである、という立場を明らかにしたのである。

その上で拡張の余地として、

  • 動的要素を必要とする場合はクライアントサイドスクリプトを
  • 視覚的要素を必要とする場合はstylesheetを

使用するように求めた。 ちなみに、stylesheetとはイコールでCSSのことではなく、それがわからない人はJSSやXSLTについて調べておくとスムーズに理解が進むだろう。

HTML4は至ってstableであった。 諸々の問題はあれど、それらは取り込まれることなく進んでいた。 XHTMLにおける変更は、微々たるものであると言って差し支えあるまい。

だが、WWWの世界ではその間に様々なことがあった。 HTML4以降の歴史を動かしたのはGoogleであったと言って差し支えないだろう。 Ajaxが注目されるに至るまでの流れもGoogleのものであったし、HTML5策定時のGoogleに影響力は果てしなかった。

そして、Googleは明確な「オンライン派」である。 そもそもユーザーが直接にドキュメントを扱うことを良しとしない。だから、「HTMLをオンラインのものにしたい」と考えた。 これに対して反対する声、つまりレガシーなTeXのようなHTMLを尊重すべきだと言う声も強く、初期ではどちらかといえば「ドキュメントフォーマットである」という既定路線を維持する状況にあった。

だが、WWWがさらに成長し、webアプリケーションが基幹を握るに至り、すなわちGoogleが世界を支配するようになると否とは言えなくなってくる。 最初から「オンラインHTML」寄りだったAppleの存在の存在も多少あるが、この過程においての存在感は小さい。結局のところGoogleに対して否を唱えられる存在は既におらず、競争力という点も鑑みてMozillaがこれに同調したことにより、HTML5は「ウェブアプリケーションを提供する基盤」として位置づけられるようになった。実際、CSSには大手ウェブサイトが導入しているトレンドデザインをより簡単に書く方向となり、ウェブレンダリングの挙動はこれらを優先する(これらとことなるレイアウトデザインを許容しない)方向に振れた。

だが、必ずしもそれをよしとしない考え方もあって、HTML5というのは様々な思惑が混ざりあった言葉になった。

HTMLとCSSとJavaScript(ECMA Script)

HTMLとCSSはW3Cが、JavaScriptの仕様であるところのECMA Scriptの仕様はECMAが標準を策定している。

重要なのはこれらが「独立したもの」であるということである。 HTMLがCSSとJavaScriptと共に使わなければならないわけではないし、CSSがHTMLと共に使わなければならないわけではない。 それぞれが独立したものであり、これらは「ハンバーガーとポテトとコーラ」のような関係であるといっていいだろう。

独立した存在であるというのは、それ以外が存在しなくても成り立ち、なおかつそれ以外のものとも組み合わせられる、ということを意味している。

そもそもECMA Scriptに関していえば、JavaScriptの標準というわけでもない。 JavaScriptはECMA ScriptにないDOMに関する機能があり、こちらはW3Cが策定している。 結果として、JavaScriptに明確な言語仕様というものがなく、複数の(ある程度の互換性を持つ)実装が存在するに過ぎない。

ちなみに、「JavaScriptの実装」というと、「ChromeとFirefoxでしょ」と言う人がいるのだが、これは間違っている。 まず、Crhomiumが採用する(そしてNodeやElectronも使う)v8 JavaScriptエンジンがあるが、MozillaはC/C++で書かれたSpiderMonkey(Firefoxで使われているもの)のほかに、JavaでかかれたRihnoを持っている。 Safariが使っているJavaScriptエンジンはKJSソフトウェアがベースでv8とも別物。 Internet Explorerの最終的なエンジンはChakraで、Edgeも独自の実装(EdgeHTML)だった。さらに、OperaはPresto(JavaScriptエンジンのコードネームはCarakan)を採用していた。 Prestoは独自エンジンであり、プロプライエタリなので現存しないが、KJS SoftwareベースのJavaScriptエンジンを使うブラウザは(プロジェクトが生きているかどうかは置いておいて)存在するし、w3m-jsなんてものもあったりする。

webから独立したJavaScriptといえばRhinoを搭載するソフトウェアが実は結構多い。 Javaで書かれているアプリケーションから利用するのが割と簡単なので、SpiderMonkeyが圧倒的にウェブで使われているのに対し、Firefoxでは使われていないRhinoはアプリケーションでプラグイン機能を実現するために使われるようなことが多い。

また、Node.jsもv8ベースで有名な非ウェブのJavaScript利用例と言えるだろう。 ElectronになるとHTMLやCSSも含めてChromiumの機能を利用する形なので、非ウェブと言い切っていいのかどうか疑わしいが、それでも非WWWであるのは間違いない。

CSSに関してはほぼHTMLに依存している。XMLで使うこともできるのだが、この場合「XMLをHTML扱いする」という意図になってしまう。 このような場合はどちらかといえばXSLTが好ましく、XML CSSは基本的にウェブブラウザでXMLを表示するためのテクニックだ。

ただし、HTMLの外観を定義するのはCSSだけというわけではない。 JSS(JavaScript Stylesheet)というのもあるし、これ以外に関しても仕様上制約されているわけではない。 JSSが使われることはごく稀だが、実際に限定的なHTML(あるいはそのサブセット)に対しCSSでもJSSでもないスタイルシートを定義している場合が見られる。近年はレンダリング部分を既存のものに委ねる傾向が強くあまり見られなくなってきてはいるが。

HTMLの用途と位置づけ

ウェブにしか関心のない人は知らないかもしれないが、HTMLは広くドキュメントフォーマットなのである。

基本的なものであるにもかかわらず、ドキュメントフォーマットというのは今に至るまで以外なほど発展していない。 Microsoft Wordの強さというのもあるだろうが、ドキュメントフォーマットが「印刷向け」であるのも大きい。

純粋なドキュメントフォーマットとしては、現在使われるものとしては

  • PDF
  • PostScript
  • DVI
  • Microsoft Word
  • Open Docuemnt
  • RichText
  • roff
  • ePub
  • DocBook
  • HTML

あたり。 それらを生成するフォーマットは多くて

  • Markdown
  • GFM
  • PHP Markdown Extra
  • Pandoc Markdown
  • その他Markdown方言
  • ReSTructured text
  • TeX
  • LaTeX
  • POD
  • RDoc
  • Plain2
  • Asciidoc

などなどかりなたくさんある。

これらは「印刷か表示か」という点で性格の違いがある。 例えばLaTeXはプリプロセッサが処理するための正しい情報になる論理性があるが、出力自体は「太字」など視覚的な定義になっている。 これは、印刷してしまう場合、内部的にどのような情報を持っていたとしても失われてしまうことになるので、視覚的な表現が重要になるからだ。

manroffなどは端末上で表示するためのものだから、端末で表現可能な内容に偏っている。

Windowsヘルプフォーマット(.HLP.CHM)やDocBookなどは対応する環境が少ない。 結果的にオンラインドキュメント1で汎用性・交換性が高い形式というのはHTMLに限られてしまう。

次いでePubということになるだろうが、ePubは書籍を前提としており、オンラインドキュメントに最適化されているとは言い難い。2

この状況で、人に配布する整形済みドキュメントフォーマットとしては「HTMLが望ましく、HTMLしかない」という状況が存在している。 もちろん、ここではHTMLのサブセットはHTMLに内包するものとする。

webの世界というのはドキュメントを基本とするものであり、「みんなアプリケーションなんだからHTMLもアプリケーション化すべき」などというのは乱暴を通り越して愚かである。 HTMLの世界はwebに限らず広がっている、というのは、基礎教養と言えるだろうし、それ以上に自分の了見でのみ物事を定めようとすることを愚かと呼ばずしてなんと呼ぼうかという思いだ。


  1. オンラインドキュメントとは、コンピュータ上で読むドキュメントという意味であり、ウェブという意味ではない。

  2. ちなみに、ePubはそもそもXML/XHTMLベースであり、CSSを使用するものだから独立したフォーマットと呼べるかどうかはあやしい。

はやわかり レスポンシブルCSS

まえがき

リクエストがあったので、レスポンシブルウェブサイトを構築するCSSの基本的な考え方を簡単にまとめよう。

これは、CSSやHTML自体を書けない人を対象にしたものでは ない

ケース分けの仕方

CSSのメディアクエリを使用する。 メディアクエリの詳細についてはMDNに記事がある

ほとんどの場合メディア特性にはwidthまたはheightを使用する。 広く使われてはいないが、aspect-ratio, orientationは基本的なメディア特性であり、非常に有用である。 この両者を組み合わせることで(ゲーム画面のように)スクリーンサイズに合致するレイアウトが可能になる。

典型的なケースでは、レイアウトボックスが1000px幅だとして、ビューポートに1000pxがなければ画面いっぱいにfallbackする。

ただし、このようなケースでは次のように書くべきだ。 そして、実際にこのように書くべきケースは非常に多く、メディアクエリを必要とするケースは表示切り替えくらいのものである。

この場合、#MainBoxは幅1000pxをまず確保しようとする。 しかし、min-widthによって制約されているため、1000pxの幅が包括ブロックの100%を越える場合は包括ブロックの100%に留められる。 これによってビューポート幅を突き破ることがないようにできる。

widthによって判定するのは「コンテナの中央寄せ」という、2003年以来の文法に従っているためだ。 だから、しっかりとデザインするのであればまた異なった設定になるだろう。

HTML構造

基本的なレイアウトでは全体を収めるためのボックスを用意する。

この中にレイアウトしていくのだが、基本的には「左であり、上である」「右であり、下である」という順序で書く。 これを入れ替えるのはCSSでは少し難しい。 ただ、良いCSSを書くには「可能な限りJavaScriptに頼らない」という気持ちは必須である。

「本文コンテナとサイドバー」という構成であれば、サイドバーの内容を上にしたいのであれば左サイドバーになるし、サイドバーの内容を下にしたいのであれば右サイドバーになる。

横並びレイアウトで最も基本的なのは「コンテナをテーブルに、コンテンツをセルに」である。 この場合、例え縮小しても横並びは維持されるし、min-widthなどではみ出す場合、そのままoverflowする。

コンテナ側のサイズが決まっていて、サイドバーのサイズを指定しないことでサイドバーをある程度縮小させることを許すことができる。 これは、最低限必要な幅を確保した上で、それよりも幅があるのであればもう少しスペースをとって表示することができる。

次のCSSでは、1000pxを下回るのであればtableによる表示を諦めるが、ボックス自体は最大1200pxまで伸張する。 1200pxの場合、その割合としてはサイドバーが200pxから300pxの間で、残りがメインになる。

十分なスペースがないときに右のボックスを下に送るのであれば、メディアクエリは必要ない。 inline-blockとして配置することで、overflow時はボックスを並べないようにすることができる。

この場合、サイズ指定は並んだときに確保すべきボックスと、単独になったときに伸張すべきボックスの大きさを意識する必要がある。 また、内包されているボックスの位置と大きさはいずれも#MainBoxにbindされていることを忘れてはいけない。

表示ボックスそのものの変更

ボックスのレイアウトではなく、内容そのものをレスポンシブルに変更したい場合はdisplayを上手に使うといい。 例えば次の場合、#TOCは十分な幅がないとき省略される。

メニューは十分な幅があれば横に配列しようとするが、ないのであればそのまま縦に配列する。

同一の内容に対して異なる表示を提供したい場合は、予め複数書いておくようにして、メディアクエリでdisplay: block;display: noneを入れ替えるのが良い。 この場合、同内容はジェネレータによって生成されるべきである。PureBuilder Simplyの場合、Pandocテンプレートを使うことにより内容の反復を避けることができる。 より一般的にはテンプレートそのものをeRubyなどで生成し、値を置き換えるのが良いだろう。

順序の変更も、「複数書いておいて、表示を切り替える」のが最も無難である。

がんばるのであれば、position: absoluteあるいはposition: fixedを使うことで「見かけ上の順序」をごまかすことができる。 これらはビューポートをそのまま使うブロックに対して指定することが多いためあまり意識しないだろうが、座標起点は包括ブロックであり、また包括ブロックとしても機能する。 以下の例では十分な幅があればサイドバーは左に表示されるが、十分な幅がないとき、サイドバーは下に表示される。

表示コンテンツの変更

レスポンシブルに異なるバナーをコンテンツ中に表示したいような場合は、JavaScriptを利用するほうが良い。 ただし、メディアクエリと背景画像を使うことでCSSで処理できないこともない。

各ページ共通のものであればテンプレートに組み込んで表示ボックスの切り替えが良い。

表示コンテンツの表示の仕方でいえば、max-width, min-width, そしてmarginの値をコントロールするようにするといいだろう。

おまけ。ビューポートを全部使う

文字主体のコンテンツの場合、文字サイズをビューポートに基づくようにすれば問答無用でビューポートいっぱい使うデザインが可能。

5vwは一応、1行に20文字入る計算になる。 この場合、横幅が小さい場合は文字数が、縦幅が小さい場合は行数がある程度確保されるようにするという基準になっている。 その上でなるべく大きい文字にする。画面いっぱい使って大きく文字を表示するのでお年寄りにもやさしい。

標準的に1920pxに対して16pxの文字とすると120文字入るので0.85vhとなるのだが、これをすると非常に小さくなってしまう。

これをやると拡大縮小がかなり制限されてしまうことから、「大きく文字を表示する」前提で考えたほうが良い。 2vhくらいあればちょっと大きめに表示されるが、拡大できないので2.5vhくらいをスタートに考えたほうが良いかもしれない。 特にウィンドウをタイルしたときなどで縦長になるとすごく小さい、などということもありえるのだ。

Mimir Yokohamaで続く改修、力を注ぐ

2018年8月5日、Mimir Yokohamaのソースリポジトリにはじめてのタグ、5.0が打たれた。

なぜはじめてのタグなのに5.0だったのか。

これは、Aki SI&Eのプロトタイプを1.0, Aki SIEのウェブサイトを2.0, Mimri YokohamaのWordPressを3.0, 現在のウェブサイトになったときを4.0とカウントしたものだ。

つまり、はじまって以来の、同一システムのままのバージョンアップとなった。

しかもこのタイミングである。 先日、大幅な編成変更と、いいね機能、コメント機能の追加を行った。 これで新しい船出だというタグではない。かといって同バージョン最終仕様としてのタグでもない。

これから次々とアップデートが予定されている。 その中での一区切りだった。

13年積み上げてきたものとは

PureBuiler Simplyの原型となっているのは2005年のACCS1である。

もうブログが流行り始めていた頃だったが、既に「事前生成戦略」に関するイメージはあった。 ブログを避けた理由は、一連の流れを持つ記事群を拾いにくくなること、そして時間とともに消えてしまうことだった。

「普遍的な内容を、きちんと分類して読みやすいように提供したい」と考えたわけだ。

様々な機能、様々なアイディアがあった。 実装されたものもあるし、実装されなかったものもある。 言語もPerl, PHP, Zsh, Rubyと変わってきた。 このような変遷をたどっているのはEQAIとこれだけで、まさに私のライフワークであり、また成長の軌跡でもあった。

この中でこだわってきたものもある。

例えば、デザイン性を保ったまま軽量・高速なウェブサイトを構築すること、可能な限り高いアクセシビリティを提供すること(環境や回線の違い、身体的ハンディキャップなどで差別しないこと)などもそうだ。 意味あるコンテンツを、読みやすい形で提供する、というのもある。

ACCSが追い求めてきた機能は次のようなものだった。

  • カテゴリで分類されて探しやすいインデックス
  • 検索機能
  • 意思表示機能 (コメント、と考えていることが多かった)
  • 連続した記事のページめくり
  • 要約の先読み
  • インライン用語集
  • prev, next, glossary, index, description情報を持たせる

PureBuilder Simplyは当初、Mimir YokohamaのWordPressページで提供されている全機能を提供する、ということを目標としていた。 これについては既に達成されている。そのために、従来のPureBuilderにはなかったタグ機能なども追加された。

「いいね機能」「コメント機能」はWordPressに完全な意味で標準であるものではないのだが、事実上付属するようなもの(特にコメント機能は)なので、これを追加したことで、事実上「WordPressを置き換える」というミッションは完遂した。

だが、同時に既にWordPressにはない機能の搭載もはじまっていた。 用語集機能はWordPressではなく、PureBuilderの伝統に由来する。そう、ACCSが目指していたものを達成する、という次のミッションに向かいはじめたのだ。

そこでつけられたのが5.0タグだった。

用語集機能

用語集機能という発想のスタート地点は、私が使っていたWindows 98SEマシンに搭載されていたインライン翻訳ソフトだった。

カーソルを載せるだけでその単語を翻訳してくれる、というソフトウェアは今持ってそれ以上のユーザービリティを提供するものはない。

「カーソルを載せたらわからない言葉を教えてくれる」というのは最高に便利だと思ったのだ。 現在はニコニコやはてなが似たような機能を提供しているが、あれはページが変遷してしまうため私からすれば理想的ではない。 どらちかといえばWikiに搭載されているインライン展開のほうがずっと理想的だ。

この機能は

  • 表示後にJavaScriptでtreatする
  • 生成時にHTMLを置き換える形で組み込む
  • ソースドキュメントを改変してから生成する

という3つのパターンを行ったり来たりしている。

PureBuilder SimplyではPost Plugins/Pre Pluginsの構造からこの3つとも選択肢として取ることができる。 Mimir YokohamaではPost PluginによるHTML置換え方式を取っている。

このあたりは試行錯誤の成果といえるだろう。 用語集ページも含めてYAMLの辞書ファイルから生成しており、文書に対して特別な処理は必要ない。

次の記事、前の記事

ACCSで最も苦戦したのがこの機能だ。

この解決については何度か言及しているが、今は前後関係をメタ情報として書く、という仕様になっている。

解決方法としては後退しているように見えるが、前後関係の自動解決はファイル名なり、もしくはなんらかのヒントなりを厳密に管理する必要があり、結構バグりやすいということを経験したのだ。

複雑な置換えやユーザーの管理によってバグを発生しうるようなものであるならばメタ情報をユーザー自身が書くべきだ、という割り切りは、これまでの歴史の中で手にしたバランス感覚である。

実のところ今の構成では前後を自動化することは難しくない。 だが、ユーザーがそれを守ってくれることを期待すべきではないだろう。

Post pluginsはページ生成が終わってから実行されるが、これは「他の記事の情報を取得できるようにする必要がある」からであり、 環境変数$pbsimply_indexesという形でデータベースのパスが渡っていることから

のようにして取得できるし、 連番に限るのであればもっと簡単に

とできる。 ちなみに、ディレクトリを認識させる方法は最新のコミットで環境変数$pbsimply_subdirに含まれるようになった。

このように「できるけれど、あえて手書き」だ。 これは、問題を簡単にするためと、この処理をPandocテンプレートを通じて行いたいためだ。 Pre Pluginsを使えばできるが、かなり複雑なことをすることになるため避けている。

意思表示機能

過去には「コメントを直接にHTMLファイル化し、objectで読ませる」ということをしていたこともある。

表示するかどうかを別として、コメント機能はそのときとあまり変わっていない。 表示させるために必要な部分を削ってシンプルになったくらいだ。

このような機能は本質的な部分は極めて簡単に書けることはこれまでの経験によって証明されている。 どの程度正当性を検証する必要があるかという点がwebアプリケーションの分量になる。

要約の先読み

まずは要約を入れる

いよいよ今回のハイライトだ。

descriptionへの対応自体は最初のリリース時点で

という記述があり、対応はちゃんとしていた。 だが、「書くのが面倒」「書いてもあまり意味がない」ということで放置していた。

「descriptionの先読み」は今まで実装計画には入っていたが、実装されたことはなかった。 そもそもdescriptionってSEOのために入れられているくらいで、「descriptionを読ませる」という発想はあまりない。

Firefoxだとこんなふうにブックマークのプロパティを表示するか、ブラウジングライブラリー上で詳細表示にするとdescriptionが表示されたりするのだが、これを見たことがある人という人は地球上に5桁いないのではないだろうか。

Firefoxのブックマークの詳細

だがdescriptionは入れたいと思っていたし、それを活用したいと思っていた。

そもそもの発端はトップページのレイアウト更新で、最新の更新記事と要約を(ニュースとは別に)表示したい、ということだった。 「どうせ記事の要約書くんだったらdescriptionに入れようよ」ということだ。

実はAtomフィードもスタンバイしている。

要約を見える形に

だが、これだけではおもしろくない。どうせ要約を表示するのならばぜひともユーザーに見える形にしたい。

私の文章は基本的に長いので(体系的でない短い文章に価値を感じていない)、読むのがしんどい人もいるだろう。 読むかどうか決めるために要約は重要だ。

要約を見たいタイミングとはいつだろう? やはり記事を読み始める前だろう。ならば本文前に

とかやってやればいいし、そのほうが効果的なのかもしれないが、既に「文書情報」という項目があることを考えるとちょっといただけない。

そこで文書情報に追加した上で「記事タイトルをクリックすると文書情報にジャンプする」という仕様にした。

これはヘルプページにも書いてあるけれども、誰も気づかなそうだ…

もうひとつ、利用者は多くなさそうだが、カテゴリインデックスがある。 ACCSとしてはこれが中心であり、ぜひとも使って欲しい機能だ。 世の中、情報を整頓するということに怠けすぎて、検索が全てになってしまっているので、使われていないような気もするけれど…

しかし私の意図としてはこのようなインデックスを活用してほしいというのがあるし、やはりタイトルだけではわかりにくい。 かといって変遷するとだるいので、変遷せずにインデックス上で要約を確認できると便利だ。

これはタイトルで関心をそそられた後の二次的な情報であり、通常は一覧性が高いほうがいい。 というわけで、ツールチップにしてみた。

PureBuilder Simply ACCS上でDescriptionを扱う

単純には記事タイトルにtitleで入れてあるため、ロールオーバーツールチップとして表示される。 だが、スマホだとこれが効かないので、補助的に“📖?”と表示して、これをタップすればツールチップが表示されるようにしてみた。 全く標準的でないインターフェイスなので、あまり気づいてもらえないような気もするけれど…

PBS ACCS用ツールチップ実装

何度見てもJavaScriptの複数代入が慣れない。 慣れればみやすそうだけども。

世の中的には割と珍しいDOM操作をしているが、これはelementに対してイベントリスナを設定するためで、innerHTMLだと二度手間になる。 基本的にやっていることは「記事部分 > リスト全体 > リンク」と絞り込んでいって要素を作成して追加する、という手順だ。 末っ子要素を追加するとき(今の要素の親要素の最後の子要素として追加する)はelement.parentNode.appendChildという手順は覚えておいてもいいかもしれない。

glosarryと共通のコードがライブラリとして読まれるようになっている。ライブラリはdeferだがasyncではない。

900pxを堺にしているのは、「サイドカラムがあるのであれば表示領域は少なくとも右側にサイドカラム幅はあるが、シングルカラムになるとそうではない」からだ。(このページのシングルカラム境界は800pxである)

機能チェックしているが、document.addEventListenerできないブラウザでJavaScriptに対応しているものはあまり残っていないだろうし、あっとしても単純にイベントリスナー設定時にエラーになるので放置してもいいかもしれない。 ただし、間違って複数回ライブラリが読まれたときのためにArt.tooltipはしておかないとイベントリスナが複数設定されてしまう。

PureBuilder Simplyはうまくいっている

PureBuilder Simplyがここまでうまくいっている理由としては、やはりPandocの強力さがなによりだろう。

PureBuilder SimplyはPandocが持っている機能をちょっと拡張する…という考え方をしている。 今までドキュメントジェネレーター自体を制作していた(PureDoc)ことと比べると問題はかなり簡単になっている。

Pandocの動作は必ずしも簡潔ではなく、自分で実装するのであればドキュメントジェネレーターにここまでの機能をもたせることはないだろう。 だが、Pandocがある以上はPandocを使いたい。

もしPandocがなければdocutilsを拡張することを考えただろうが、その場合はPureBuilder Simplyは今のように良いツールにはなっていなかっただろう。

PureBuilder Simplyが今ほど素晴らしいツールになったのは、Pandocがあったからこそだ。

Mimir Yokohamaに対して行われている様々な拡張は今の所PureBuilder Simplyに対して適用されていない。 これは、PureBuilder Simplyが生成するものに対する機能ではなく、Mimir Yokohama固有の、そしてテンプレートとCSSによるものだからだ。

だが、いくらかでも一般化してPureBuilder Simplyに還元していければと思っている。 PureBuilder Simplyのエコシステムの充実は普及には不可欠だろうから。

Mimir Yokohamaに「いいね機能」「コメント機能」を追加

概要

Weekly 10000PVを達成して機能強化に力の入っているMimir Yokohamaのウェブサイト。

連続の機能強化でついに「いいね機能」と「コメント機能」が追加された。

実は先日の「お問い合わせフォームの実装」は単にその機能を実装する最小限ではなく、簡単なアプリケーションを実装できるプラットフォームになっており、 アプリケーションを追加する条件が整っていたのだ。 また、そのためのテストもしてあった。

そのため、実は今回コード追加はわずかで、両方合わせても23行ほどにとどまる。 ごく簡単だが、確証が持てないためにテストと本番環境のための修正を行ったりして結構な時間がかかった。

これに関してはみるべきところはあまりない。 受け取ったパラメータをファイルに書き込めば良いだけだからだ。

ちなみに、連打しやすいアプリケーションを入れるために連打の対策もサーバーにしてあった。 実は先のパフォーマンスチューニングはこの対策によってパフォーマンスが低下してしまったため、これをカバーするついでに行ったものだった。

いいね機能の設計

いいね機能はごくシンプルだ。

Pandocテンプレートを使ってページタイトルを埋め込むことができるので、これだけ使うのであれば単にテンプレートの中にフォームを書けばいいだけ、ということになる。

実際はリファラ(Rack::Request#referer)及びユーザーエージェント(Rack::Request#user_agent), IPアドレス(Rack::Request#ip)も特定に使用している。

ポイントは「一度送信したら表示を変更、送信を無効化する」ということだ。

inputタグのdisabled属性を使うことで送信ボタンを無効化している。

そう、この機能はHTML上でインラインで書かれているのだ。 このような書き方はW3C的には推奨されないのだが、Googleは推奨している。 実際、これだけのためにJavaScriptファイルをロードさせることをしたくなかったので、インラインにした。

ポイントは

  • Legacy DOMにおいてフォーム部品は連想配列のようにアクセスできるようになっている
  • submitボタンのラベルはvalueである
  • disabledによってフォーム部品を無効化できる

である。

これらの処理は送信の「前に」行われる。 これは正しいことではないが、問題はない。 なぜならば

  • 送信できなかったからといってユーザーが修正するなどの手を入れる余地はない
  • 特に返信を必要とするものでもないので、送信失敗はクリティカルな問題でもない
  • サーバーエラーなどは表示されるようになっている

からだ。

また、画面変遷せずに送るだけ…というと、Ajaxで非同期に送るしかないように考えるかもしれないが、 実際は現代のブラウザは基本的に2XXステータスで空コンテンツを返すとページ変遷しないようになっている。

このような用途のために204(No Content)が用意されているため、204を返す仕様だ。 できるだけブラウザの標準機能に頼るようにしている。

このボタン、めっちゃgooglebotが押してくる…

コメント機能の設計

公開されるものではなく、sanitizeしなくても問題が発生しない設計になっている(単に文字列として扱う以上の取り扱いがなされる条件で使用しない)ため、非常に楽だ。

難しく考えるよりも、シンプルで挙動をちゃんと把握できていて、余計なことをしない方法をとるのが最も楽に、確実に、バグなく設計できる。

ただ、この機能に関しては「コメントフォームの表示」などが必要になり、JavaScriptが必要になった。 また、コメントするというあまりとらない行為のための部品であり、常にロードすることは非常に好ましくない。

そこでとった方法は「JavaScriptの遅延ロード」であり、「クリック時にelementを追加してJavaScriptを読み込む」という方法だ。

これでscript要素を追加している。

では呼び出されたあとどうしているのか…というと、こんな感じだ。

そう、コメントフォームは標準でHTMLに含まれていない。

パフォーマンス的にみても親切機能のためにもう重くなりつつあるドキュメントをこれ以上重くしたくないので、 「フォーム部分はJavaScriptがロードされたときにinnerHTMLで書く、という方法をとっている。

「クリックされたときにはじめて必要とされるのでドキュメントノードを追加してロードする」という手法は稀に使われるが、さすがにそれでHTMLドキュメントそのものを生成するというのはまず見ない手法になっている。

連打されたときのことを考えて、clickイベントを発生する値に特殊なプロパティを埋め込み、何度も実行されないようにしている。 なお、これは本当に連打したときだけ機能するもので、JavaScriptシングルスレッドなので一回このスクリプトに入っていまえばこのスクリプト中に割り込まれることはない。 だが、連打されるとイベントがキューに入ってしまうので何度も実行されてしまうことから、そうしたことがないようにしている。 シングルスレッドなので、このスクリプトが実行されているのにプロパティが設定されないうちにまた実行されるということはない。

元になるフォーム自体は存在していて、submitボタンを配置すればフォームの送信は可能だ。 そのため、submitイベントに対するイベントリスナーを設定するものはHTML上に静的に存在している。

その上で「フォームはやっぱり閉じられたほうがいいな」ということでボタンクリックに対するイベントの変更、及びボタンラベルの変更を行っている。

送信を行った場合はもうコメントフォームは使わないので、コメントフォームは非表示にして(削除はしていない)ラベルを変更している。

ラベル変更だが、input部品とは違ってbutton要素は子要素テキストノードがラベルになっているので、dataプロパティの書き換えによって変更している。

もともとHTML上でLevel0 DOMイベントを使っているので、スクリプト上でもLevel0 DOMによってイベントを変更している。 いつもaddEventListenerを使っていたので、珍しい。 イベントの削除はイベントにnullを代入するだけだ。

なお、HTMLで全て含めてしまえばJavaScriptは排除できるのだが、これ以上余計な要素を組み込むことはできれば避けたいためこのような仕様になっている。既にDOMコンパイルは割と重い。

また、デザインポリシーからいって、多くの場合余計なコメントフォームを常に表示させておくというのは美しくないとも思っている。 JavaScriptを使わずCSSでオンオフするようにもできるが、そうすると今度はボタンのテキストを変更するのが難しいし、連打や再送信を防ぐのも難しい。

このことから、なにがなんでもJavaScriptを使わないのが正義、ということでなければ、 このような付加コンテンツはユーザービリティの点からも素直に使うべきだと思う。

こちらもいいね機能同様、送信する前に状態を変更してしまい、成功すれば204を返す仕様。

Legacy DOM と DOM Level0 Eventに関して

Legacy DOMやDOM Level0 Eventについて意見をもとめられることがたまにあるのだが、私としてはあまり勧められないと考えている。

非常に簡単なので学習にはいいが、Legacy DOMはHTMLの構造に依存する。例えば

として、

とかやってしまうと、ドキュメントの構造が変わるたびに修正だ。

だが、「フォームにのみnameで使い」「フォームはW3C DOMで特定する」のであれば悪くない。

DOM Level0 Eventはイベントリスナーが1つしか登録できないためモジュール設計になっている場合や、なんらかのライブラリを使っている場合は使ってはいけない。 イベントを「追加」する場合はLevel2で、イベントを「設定」する場合はLevel0という使い分けも考えられる。 HTMLに直接書く場合はLevel0しか書けないので、その部品の基本的な動作と定義されているならLevel0でもいい。

ただし、その場合でもできればLevel2 Eventを「後から追加する」のが適切なので、DOM Level0 Eventの使いどころは今回のようなものが唯一だと思う。

Chromiumのインスペクタにいつの間にかモバイルデバイスインスペクションが入っていた

web分野の人にとっては当たり前の可能性もある知識だけれども、Chromiumのインスペクタにモバイルデバイス向けの検証機能がついていた。

Chromiumのモバイルインスペクタ

これをオンにすると、モバイル的な画面サイズとなり、同時にタッチエミュレーションが行われるようになる。 レンダリングが調整されるということはなく、あくまでこの2点が変更される。

モバイルインスペクタのガイド画面

Responsiveモードではサイズを柔軟に変更することができる。 実際に小さな画面ではどのように表示されるのかということをエミュレートできる。

また、デバイスごとの画面設定も可能。 候補は少ないがiPhoneは入っている。

機種や回線をエミュレートできる

ここで言う解像度は論理ピクセル数である。 例えばiPhone8の画面の実ピクセル数は1334×750だが、668×375と認識されている。

これはスケーリングによって1pxを実際の1ピクセルと対応させない、というもので、先日書いたので参考にしてほしい

拡大率は画面表示のサイズを変更するもので、リアルサイズに近づけることで物理的な大きさもチェックできる。

上部にあるガイドは各種デバイスに合わせた表示状態をエミュレートできるものになっている。 クリックするとResponsiveの状態で各プリセットにあったものになる。 Laptopが1024px, Laptop Lが1440pxとなっており、この上に4k2560pxもテストできるようになっている。

また、ネットワーク状態もエミュレートできる。 低速な回線ではどれくらいロードにかかるのかということもテスト可能だ。 offlineはページをロードしたあとにofflineにし、Ajaxが使えない状態をテストすることができる。

Low-endでMimir Yokohamaが4秒程度なのに対し、Chienomiは15秒ほどかかったので、その高速さにだいぶ満足している。

デバイスの論理ピクセル数なんて調べるのはとても大変なのでプリセットが入っているのはとても嬉しい。 サイトの設計にもよるが、私の設計だとアップロード前に十分なテストが行える。

Arial/Helveticaを置換え 読みやすい欧文をウェブに

本当にArialが見やすいと思うのかい?

さて、突然だがあなたに質問だ。

次の画像を見てあなたが読みやすいと思うのはどれだろう。 「見やすい」ではない。ちゃんと英文を読んで、読むのが楽だ、このあと延々百何十ページとこの英文を読むのも苦にならないと思うのはどれか、ということである。

比較1

比較2

比較3

比較4

あなたの心は決まっただろうか?

webでなぜArialを指定したがるのか

Arialが指定されている現実の確認

まずはTwitterのフォント指定を見てみよう。

次にGoogle

Yahoo! Japan。Arialは指定するがHelveticaは指定しない。 MS PGothicが先に指定されているので、かなりArialを指定している意味は恐らく全くない。ヒラギノより先にしている理由もわからず、 これはさすがに技術力が疑わしい。

さらにいえば、フォントファミリーにクォートなしでスペースが入るのも無効なはずなので、技術力完全に駄目なやつでは…?

Mozilla。Zilla Slabはウェブフォントになっている。

Facebook。Helvetica優先でヒラギノ優先なあたり、Facebookはりんご党らしい。

Microsoftコミュニティは日本版でも欧文フォントのみの指定。 それもWindowsデフォルトフォント優先だが、MacデフォルトのHelvetica Neueを優先的に指定する謎のこだわり。

震源地となったMSNは現在は指定なし。私が指摘してから変えた…?

理由と経緯

ArialというフォントそのものはHelveticaの代替フォントである。

Helveticaは1950年代からあるフォントで、すごく使われてきた。PanasonicのコーポレートロゴもHelveticaだ。

Helveticaは現在はライノタイプのフォントで、Macに入っている。 MacはHelveticaを改変したGenevaもあって、割と重用しているようだ。しかもArialもある。

Arialのほうは1982年にRobin Nicholas, Particia Saundersがモノタイプに依頼して制作したもので、Hervetica互換フォントで形は少し異なるものの文字幅は同じ。

ArialはAlternativeを含めればWindowsに古くから収録されていて、Helvetiva互換フォントとして使われてきた。 WindowsのArialはフォント名にHelveticaを含んでいるので、Helveticaと指定してもArialが使われる。

そして代替フォントであったはずが、Arialのほうが美しいとか、WinとMac両方にインストールされているとか1いう理由でArialが主流になっていく。 Arialもポストスクリプトフォントとしてはかなり初期に誕生しているし、当時はあまりポストスクリプトフォントはなかった(し、高額だった)。 指定しなければビットマップフォントが使用される可能性が高く、すごく雑なビットマップフォントが多かったから、Arialの指定は高品位フォントの指定だったといっていい。今の日本の感覚で言うと、モリサワのフォントを指定するような感じだ。特にヒラギノ。

Arialが使える前提に立っていると、UIに使いやすかった。 ボタンはピクセル数を指定しなければならないけれど、その上に載せるフォントに合わせていい感じに調整する機能はなかったから、 「ボタンの上にこの文字を載せるか指定するためにはこの文字がどれだけの幅を取るか特定できなければならない」だったのだ。 当時はDPIも固定だったから、これは表示してしまえば確実に制御できたし、そこでArialが使える前提に立てば簡単に作ることができた。

あと、ワープロ文書でも文字数と改ページの関係でボックスサイズが変わると困ることがある。 ここでもArial前提にしとけばワープロ文書のままで受け渡しができる、というわけだ。 これは今MS Pゴシックの状態でワード文書を送りつける愚か者2と同じような心理である。

ところがだ。HelveticaにせよArialにせよフリーなフォントではない。 そこでサイズが同じ「メトリック互換フォント」がArial以外にも作られることとなる。 これはもう、Helvetica互換というよりはArial互換である。

  • Nimbus Sans (URW)
  • TeX Gyre Heros (Ghostscript)
  • FreeSans (GNU)
  • MS Sans Serif (Windows)
  • Arial (Microsoft)
  • Liberation Sans (Liberation Font Project)
  • Arimo (Chrome OS)
  • Albany (StarOffice)

これでArialに対する互換性をクリアしたわけだ。

和文フォントでも若干そういうものはある。 AAフォントと呼ばれるもの(IPAモナーフォントやMonapoなど)は5ちゃんねる(当時の2ちゃんねる)上でアスキーアートを表示するためのものだが、 梅フォントファミリーがMSフォントメトリック互換であるのは恐らくワープロ文書やメールにおけるレイアウトの都合だろう。

webでの指定の話

CSS登場以前はUI部品のサイズを柔軟かつ明瞭に指定する方法がそもそもなかった。

そのためUI部品にテキストを載せる場合(画像にすると当時は重かった)、レイアウト上「横にいくつボタンを並べる」という都合があり、 これをぴったりに載せる方法がWindowsでもMacでも使えるArialを使う、という方法だった。

これが「WindowsとMacにインストールされているフォントを考慮する」という誤った理解となり、思考停止したままWindowsとMacのフォントをそのまま指定するようになったのが「システムデフォルトの日本語フォントを指定する」問題だ。

挙げ句、MS Pゴシックはまだしもヒラギノとは全くデザインが合わない、単に美しくない状態となるだけの「欧文フォント指定があるから、それを消さずにその後ろに日本語フォントをWindowsとMacで並べればいいよね」という最高に頭の悪いアクションが生まれる、というわけだ。

だが、和文フォントをシステムデフォルトのものを指定するのが 全く意味がない のに対して、Arialを指定することは前述のように多少意味がある。 ただし、現在はCSSを使ってUIサイズを文字サイズ基準で指定できるため、まともなデザインセンスがあるのであれば問題にはならない。 そう、デザインについて数字でしか理解できないエンジニアが解決方法としてフォント指定に走るのはまだわからなくはないが、デザイナーがそれをやったら完全にデザインを投げ捨てているということになる。

基本的に「なり」のデザインになっていれば特に指定する必要はないのだが、UI上(好ましいことではないが)ボックスの中に入っていないテキストなどを書く必要がある場合はArialを指定するのは意味のあることである。 もちろん、その場合でもGoogleがしているように

とするのが適切。

ただし、google検索においてArialの指定が必要かどうかは疑わしい。 ただし、英語で検索するとGoogle検索の結果はArialの場合は2行であるため、2行に内容を詰め込むためには詰め詰めのフォントを使わせたい、というのがあるのかもしれない。そうでないフォントを使うと3行になったりするからだ。 その意味では「ゆるやかに意図を使える指定」ということで、ある意味妥当である。

Google検索の英語での結果 (Raleway)

このArialと汎用のサンセリフを指定する方法は日本語であっても有効である。 日本語はユーザーが設定したサンセリフ体が使われるだろうし、設定していなくても適切な日本語フォントが選択されるため、全く問題ない。

だが、そんなことを理解せず、思考停止してArial > Helvetica > MSPゴシ > ヒラギノにしているサイトが あんまりにもあんまりにもあんまりにも多い ので、3現実的に美しく読みやすく表示させようと思ったら、 「ArialとHelveticaとMS Pゴシックを置き換える」必要がある。

FontConfigに関してはウェブブラウザは当該フォントがFontConfigのエントリ上存在する(物理的にはなくてもいい)場合にその第一候補だけを取得するので、Arialに複数のフォントを設定しても駄目。 そのためArial+MS Pゴシックで合成されることを前提とした設定を行う、ということになりそうだ。

せめて日本語サイトで欧文フォントを指定していなければArialを英語専用で考えることができるのに、langも使わずまぜおって…

UI部品がArialでないと困るという場合でも、それはUI部品をクラス指定して、あるいはUI部品のタグで指定してフォントを指定すべきで、bodyに指定すべきではない。

デザイン的に指定したい (今の時代にデザイン的にデフォルトフォント、かつArialを指定したいとかセンスが死んでるけど4) 場合も「共通で入っている」とかじゃなくウェブフォントにすべき話だ。 和文フォントは大きすぎるため非常に迷惑になるが、欧文フォントは小さいので結構平気なもの。 Roboto, Open Sans, PT Sansあたりはキャッシュされている可能性が高いので、自前ではなくGoogle Fontsあたりを指定するのが良い。

なおWindows

Windowsでは標準でSerifやSans-serifを置換えることができないけれど、 Google ChromeもFirefoxもフォントの設定ができるし、している人は多い、少なくともユーザーにはその自由があるのでやはり強制はすべきでない。

Arialは本当に見やすいか?

冒頭の画像は

  1. Oxygen-Sans
  2. Arial
  3. Sen
  4. Input Sans Narrow

である。 私はInputが圧倒的に読みやすい。

見て気づくかと思うが、Arialはボックスに対して黒い「太めフォント」であり、極めて文字間を詰めた「詰め詰めフォント」である。 ある意味ではMS Pゴシックと類似の特性と言っていい。

Arialは「詰め詰めのフォント」であるため、代替は意外と難しい。 実際にArialは日本語フォントと比べpx数の決まったUI上に載せることが多く、サイズが違うフォントはUIからはみ出すケースが少なくないのだ。

そこでSans-Serifフォントから適当に見繕って表示させてみた。 Arialはとってもプロポーショナルなので文によって長さは大きく変わるが、おおよそこんな感じになった。 なお、試したフォントは200弱ほどあった。

サンセリフフォントの比較 (1)

サンセリフフォントの比較 (2)

サンセリフフォントの比較 (3)
サンセリフフォントの比較 (4)
サンセリフフォントの比較 (5)
サンセリフフォントの比較 (6)
サンセリフフォントの比較 (7)
サンセリフフォントの比較 (8)
サンセリフフォントの比較 (9)
サンセリフフォントの比較 (10)
サンセリフフォントの比較 (11)
サンセリフフォントの比較 (12)
サンセリフフォントの比較 (13)

この中からArialの代替になりそうなフォントを比較してみる。

Arialと代替フォントの比較

割と小さなところにも使われたりする(例えばTwitterのスクリーンネームとか)ので、グリフが小さいフォントは割とツライ。 UIフォントとして人気の高いUbuntuとOxygen-Sansは幅も似たようなもので優秀に見える。

私はlやtの字形がカーブしてくれているほうが見やすいように感じる。ここにはabilityみたいな縦棒いっぱいの単語がないが、 ぱっと見に字形を区別しやすいフォントが日本人には望ましいのではないか。日本語にはあまり「ぱっと見に字形を区別できない字が並ぶ」ということがないし、単語を字形で判断しようとするので、スペースで区切られた単語単位で識別して字は見ないという習慣があまりないためだ。

そう、日本人は日本語に慣れているが、日本語と英語では性質が違う。 日本語では一文字あたりの意味価値が高く、文字の識別が重視される。対して英語は単語感覚なので、どちらかといえば「単語の形」で見ていて一文字ずつはあまり見ていない。

Arialは詰め詰め黒々であり、単語の形で見るのであればなんとなく見やすいかもしれないが、ちゃんと文字がどう並んでいるかを識別しようとするとコストが高い。もうちょっと離すか、細くするかしてほしいし、字形も区別しやすいようにしてほしい、と感じるわけだ。 しかし字を離すと幅が増えてしまう。 TwitterのスクリーンネームなどでもArialが使われてしまうため、グリフ自体を小さくするとそれはそれで見づらい状況になる。

普通のCondenbcedでも厳しいくらい幅が狭く、代替を考えるのも難しい。

なお、字形の差については実はあまり問題がない。読みやすいフォントに代替するのであれば問題になるのは幅であり、例えばSans-serifではなくSerifを選択しても構わない。 ただし、それはデザイン的にArialが選択されている場合には違和感があるかもしれない。

Arialの代わりを探せ

もう少し長文で比較する。Arialがちょうど3行になっている。

Arialと代替フォントの比較 詳細(1)

Arialと代替フォントの比較 詳細(2)

デザイン的にも幅的にも差を小さくしたいのであればUbuntu、またはOxygen-Sansが有力候補だ。 Ubuntuは若干幅広、Oxygen-Sansは細字で詰めているため幅は逆に詰まる傾向がある。

また、リストに入れ忘れてしまったがSource Sans Proはかなり優秀な候補だ。 Source Han Sans Proの日本語部分でもあるため、かなり使いやすい。合成フォントとして人気の高いOpen Sansはやや幅が増えるが、こちらも優秀。

字形が違うものはどうだろうか。 Overlockは英文なら読みやすいのだが、日本語の中に混じっていると結構読みづらい。 Ralewayあたりだと変化が激しく若干の違和感があるが、不自由はない感じになる。

個人的に良いと思えたのはFira Sansだった。SenやAmikoもなかなか魅力的だが、幅の差がちょっと大きい。 今のところFira SansとRalewayとSenで迷っている感じだが、どうも私にはRalewayが良さそうである。

あとはもう好みの問題だ。だが少なくとも、Arialが他のなにより素晴らしいとは私には思えない。

設定例

メトリック互換

メトリック互換フォントに何を使うかはお好みではあるけども。

いい感じに表示する

和文はDPI100程度ならばさわらびゴシック、DPI150程度ならばMMCederがいい感じだった。


  1. もちろん、Helveticaで指定しても両方で使えるのだから、これを言う人は知恵が足りない。

  2. 相手がWindowsでない可能性、相手がMS Pゴシックを持っていない可能性についてわずかでも考えたのだろうか? 相手のことについて思索を巡らせず、自分に問題がないから構わないと考えるのはこの上なく愚かである。

  3. そんなこと言ってこのサイトも…と思っているみなさん。 あんまりCSSいじりたくなかったけど、直しました。アップデートでまた直すことになるのかなぁ。一応追加CSS側で上書きするようにしてはいる。

  4. 「英語のオーソドックスでキレイなフォントを指定したい」場合、RobotoあるいはOpen Sansが現代の主流。 現代は選択肢も豊富で、改良も続けられてきたので、Arialはさすがに古い。

Webページを単一のHTML (data, BASE64形式) に保存する

webページを保存するのにwgetは便利なのだけど、最近のウェブページは非常に複雑なので大量のファイルが生成されることがある。

もちろん、単純には

$ wget -p -k -E URL

でいいのだけど、ちょっと使いづらい。 単純にページを保存したい場合には、「そうじゃないんだよなぁ」と思うことがある。

あるいはテキストページなら

$ w3m -dump URL > file

という手もあるけど、これも画像が入っていたりレイアウトされていたりすると「そうじゃない」となる。

mhtmlは扱いづらいし、せっかくdata形式で埋め込めるのだから、画像やCSSを埋め込んだHTMLファイルを作ってほしい。

なんかないものかと探したところ、zTrix氏によるwebpage2htmlというプログラムが見つかった。

これがなかなか秀逸。

python webpage2html URL > file

でいいので話が早い。依存関係もpipで解消できる。 (requirements.txtも用意されている)

自動化が困難な場合にはwgetと組み合わせて二手間ほどかければ大丈夫だろう。

完成度はそこそこ。 今わかっている問題としては、サーバー側でcharsetを返さない場合、HTML内に書かれていたとしても文字エンコーディングを識別できず文字化けする。 指定もできない。

Pythonは内部文字エンコーディングに変換するため、ここでバグってしまうのだろう。 やっぱりwgetで落としてきて自前ウェブサーバーで指定するような手間が必要になる。

すごくいいツールなので、ぜひ育てていってほしいところ

メッセージフォームのサポート (Nginx + FastCGI + spawn-fcgi + Rack + Ruby)

あらまし

Mimir Yokohamaでついにお問い合わせ方法として「メッセージフォーム」が追加された。

なにがついになのか、なにをドヤっているのかと思うかもしれない。 まぁ、ドヤってはいないのだが。

実は私はかなり長い間ウェブアプリケーションをほとんど作っていない。 そして、今まで私が作ったウェブアプリケーションは、専用サーバーを持つサーブレットタイプか、もしくはCGIだった。

馬鹿にされがちなCGIだが、利便性は高く、頻繁にアクセスする性質を持たないアプリケーションには適している。

そして、そもそもウェブアプリケーションを作っていなかったのは、私が「事前生成戦略」の研究と実験に注力していたからで、 どちらかといえばウェブアプリケーションからは離れる方向にあった。 そして、ウェブアプリケーションを必要とするとしても大部分は静的ページとして提供できる方式を目指していたため、CGIで十分事足りたのである。

ちなみに、これまでウェブサーバーは

  • Apache
  • lighttpd
  • delegate
  • Nginx

という経過をたどっている。 Apacheは言うに及ばずlighttpdとdelegateはApacheよりもCGIが簡単だったので、「ほぼCGI」だった。

だが、時代は変わった。NginxはCGIをそもそもサポートしない。 私も新しい時代に対応する必要がある。

ちなみに、この作業は次の仕事のための実戦テストという意味合いもあった。

方針を考える

最も話が速いのはFastCGI Wrapである。

NginxはFastCGIをサポートしている。 FastCGIはプログラムをデーモンのように起動しっぱなしにする。

だが、通しで実行するプログラムとデーモンではそもそもの前提が違う。 そのためCGIプログラムをFastCGIとして動かすのはそれなりにハードルが高い。

そこでFastCGI Wrapの登場である。 FastCGIとして利用されるプログラムをFastCGI Wrapにする方式だ。 このラッパープログラムは要求に合わせて都度CGIプログラムをCGIインターフェイス経由で起動する。 結果的にFastCGIの意図は無視して従来型CGIを動作させるようにするというものだ。

この方法は結構出てくるのだが、基本的には既存のCGIプログラムを動作させる話である。

個人的な感覚としては、無駄なプロキシを噛ませるような方法を使ってまでCGIに固執したくない…というか、実はfcgi-wrapってそれなりにめんどくさい。

だったらFastCGI直というのもありかなぁ、と考えるわけだ。

ところが、やっぱりFastCGIはデーモン状のプログラムを想定しているわけで、やはり前提が違う。 要求として割と複雑なのか、デーモン化に関してはspawn-fcgiに担ってもらって、さらにRackを使う、というのがどうやら主流らしい。

だいぶ話が複雑になってきた。

サーバーはNginxである。NginxはFastCGIインターフェイスを経由してFastCGIプログラムにパラメータを渡し、応答を受け取る。

FastCGIプログラムはデーモンである。 Rubyでは次のようにしてFastCGIプログラムを書くことができる。

あるいは、CGIライブラリ互換インターフェイスを使うことで、#each_cgiの中身はまるっきりCGIと同じにすることもできる。

spawn-fcgiはこのデーモン部分を担う。 つまりeachしてる部分を担ってくれるわけだ。

プロセスとしてCGIインターフェイスで起動するわけではないので、fcgiwrapほどの互換性はない。 感覚はCGIに近いが、インターフェイスは意識する必要がある。

Rackはミドルウェアと呼ばれている。これはまずFastCGI抜きで話そう。

Rackはインターフェイスを担っている。 今までプログラムはCGIなり、あるいはFCGIなり、さらには各種フレームワークやサーブレットの様式(例えばSinatraとか)で書いていた。

Rackはこれらの違いを吸収するモジュール設計のものだ。 Rackに準拠したプログラムを書いておけば、たとえ愛用のフレームワークがディスコンになっても、サーバーが変わっても安心、というわけだ。

だが、Rack自身はサーバーではないからサーバーがいるのだが、Rack組み込みのサーバーというのはもう完全にRuby世界の住人だ。 だってRackはRubyのWebアプリケーションインターフェイスだから。

Passengerというソフトウェアがあって、これはwebサーバーのモジュールとしてRackに対応する。 Apacheでは比較的簡単だけれど、Nginxだと結構きつい。

そこでRackに対応したサーバーを立ててサーバーとサーバーでやりとりさせる、という方式がすごく現代的。 直接にRack経由でプログラムとやりとりするのはRackに対応したサーバーだけれど、Rackに対応したサーバーにwebサーバーとしての機能を持たせると大変なので、「本物のwebサーバーに矢面に立ってもらって、RackサーバーはあくまでRack対応に特化」というわけである。

Rackに特化したサーバーとしては(別にRackだけではないんだけど)、Webrick, Mongrel, Puma, Thin, Unicornあたりがある。

しかしRackでやりとりする方法があればいいので、FastCGI + Rackという方法もある。 それはRack側でFastCGI経由で受け取って、応答するためのハンドラが用意されている。

つまり、Unicornのようなサーバーを立てる代わりの手段としてFastCGIが使える。 FastCGIもデーモンを必要とするので別にFastCGIにすることで間に挟まってるものを減らす効果はない。 ただ話が楽になるだけである。

Unicornはむちゃくちゃ速いので、UnicornでUnixドメインソケットを使えば形式とししてはspawn-fcgiでUnixドメインソケットを使っているのと一緒だし、やっていることははるかに高度になる。 これが超モダンなやり方である。

が、あえてのFastCGI。 理由は管理する要素数を減らすためである。必要がないのにいかついものを使うことはしない。 これはサーバー運用のコツでもある。

なお、Rackに関してはかなり情報が少ない。 なんらかのフレームワーク…というか、ほぼRailsのバックエンドとしてのRackの話だけで、Rack単独の話ってない。 そして、FastCGIを使う話もない。これもだいたいなんらかのアプリケーションが「使ってる」あるいは「使わせる」話になる。

なんというか、みんなそんなに自分でプログラム作るってことをしてないのか… 世の中エンジニアたくさんいるのに、WordPressとRailsだけで満足なのか…

そんなわけで情報が猛烈に足りていない中、FastCGIとRackについて勉強することになったわけだ。

なお、Nginxでアプリケーションとやりとりする方法に関してはDiscourceで散々やったので経験済みだ。

なぜRackなのか

もちろんこのことからもわかるようにRackはなくても構わない。 spawn-cgiも使用せず単独のFastCGIアプリケーションを開発するのは容易である。

私が気にしたのはRubyのfcgiライブラリは2013年から更新が止まっているとい点だ。 また、Arch LinuxではfcgiライブラリはAURにもなく

# gem install --no-user-install fcgi

とするよりない。

ベーシックな機構であるFastCGIそのものが廃止になるようなことは考えにくいが、NginxのCGIの扱いのように消極的なサポートへと変遷する可能性はある。 その場合にアプリケーションの書き直しが発生してしまう。

Rackは現在主流であり、新規採用例も多い。 Rackが廃止になると影響を受ける範囲も非常に広いので今後10年は安泰だと思われる。

そこでFastCGI+Rackという構成にしたわけだ。 この場合でもRackはFastCGIをネイティブサポートしているわけではく、fcgiライブラリを使ったハンドラを同梱しているだけなのでfcgiライブラリは必要となる。実はこれを回避したかったのだが、結局はできなかった形だ。

とはいえ、この状態であればFastCGIを捨ててUnicornに移行するのも難しくはない。

とりあえずやってみる

Nginx

location / {
    root /var/www/testapp;
    fastcgi_pass /var/run/fcgi-testapp.sock
    fastcgi_index testapp.rb;
    include fastcgi_params;
}

Rack Application

Requestのほうはインターフェイスに絡むけれど、 Responseは単純に#finishでRackに沿った配列を返すための便利クラス。なくてもいい。

spawn-fcgi

# spawn-fcgi -U http -s /var/run/fcgi-testapp.sock /var/www/testapp/testapp.rb -n

試してるうちは-nつきにしてフォアグラウンドで実行するのが楽

実用的にする

起動スクリプト

forkingなので停止・再起動の制御のためPIDファイルを作る。

Systemd Unit

[Unit]
Description = FastCGI Rack Test Application
After = nginx.service

[Service]
Type = forking
PIDFile = /var/run/fcgi-testapp.pid
ExecStart = /usr/local/sbin/fcgi-testapp.bash
ExecStop = kill $MAINPID

[Install]
WantedBy = multi-user.target

forkingなので$MAINPIDがそのままでは使えないため、PIDFileで指定しておく。 Nginxのあとに起動しておいたほうがいいような気がしたけど、なくても構わない。 アクセスが激しい場合は逆にNginxの前に起動したほうがいいだろう

spawn-fcgi自体にはアプリをリロード、再起動するような機能はない。

おまけ

S-NailがSubjectも本文も、UTF-8をちゃんとエンコードしてくれるのですごくびっくりした。

「mailxとは違うのだよ!!!」ってことか。 さすがSMTPやPOPやIMAPにも対応しているだけのことはある。

ここの部分(MIMEエンコーディング)も自分でやるつもりだったので、かなり省力化された形。

今回の構築は他にも色々やったのだけれど、共有して意味のある部分はこれくらいのものだろう。

静的ウェブページでタグ機能を提供する

Mimir Yokohamaのページでタグ機能がバージョンアップし、完全に動作するようになった。

もともとWordPressで提供していたMimir Yokohamaのウェブページだが、独自システムに移行する際には「WordPressで利用していた機能はすべて提供する」という方針のもと、新しいサイト構築システムPureBuilder Simplyを開発して構築した。

PureBuilder Simplyは静的ページを生成するプログラムであり、webサーバーには静的ファイルを配置する。 これはパフォーマンス、セキュリティ、管理、リソースいずれにおいてもメリットが大きい。

基本的にこの考え方は「異なる内容を生成するタイミングより、同一の内容を返すタイミングのほうがずっと多い」ということに基づいており、キャッシュよりも合理的である。 一方、どうしても難しい要素もある。ひとつはページ生成パターンが無限である検索機能、そしてもうひとつはヒントを日本語のみにした場合のタグ機能だ。

検索機能はGoogleに頼っているが、タグ機能は難関だった。

タグ機能を作る

方針

  • 要求タイミングでの動的生成は行わない
  • PureBuilder Simplyの枠内で解決する。 もし解決不能な場合はPureBuilder Simplyを拡張する
  • (PureBuilder Simplyでサポートされている) eRubyテンプレートは使わない。あくまでPandocテンプレートで生成する
  • ファイル名が日本語になることはやむを得ないものとする (Nginxは日本語ファイル名に対してURIエンコーディングされたパスでアクセスできる)
  • リンクを日本語で書く(エンコードをブラウザに委ねる)ことは許容しない
  • ユーザー (この場合自分だけど) にタグに関して文書にタグ付けする以上の手間をかけさせない

タグをつける

既にPureBuilder Simplyでは 「Frontmatterに文書に関する追加的情報を書く」 という仕様となっている。1

単純にこれを反映したタグを書けば良い。

ページにタグ情報をつける

Pandocテンプレートで簡単につけることができる。

英語なら割とこれで済む話なのだけど2、日本語だと当然

みたいなHTMLが生成されてしまう。

そこで、post generate機能を利用する。 post generate機能はページを生成したあと生成ページを加工できるものだ。 基本的に第一引数として生成されたファイルパスが渡される。 このほか環境変数を通じて他の情報にもアクセスできたりするのだが、これはあまり利用を想定していない。

post generateは.post_generateディレクトリ内のファイルを順次Perlに渡す形で実行される。 Perlはshebang行を解釈するので、Perlで書かなければならないわけではない。 そして、スクリプトの出力にファイルは置き換えられる。

これは例えば

のような非常に簡単なフィルタが書けるということだ。

これを使って

のように変換してあげればタグのタイトルは日本語だがURIはエンコード済み、という形ができあがる。 もちろんスラッグへのマップを書いてもいいのだが、タグを管理するのは手間なので避けた。

タグページを作る

生成されたページの情報はindexes.rbmというファイルにRuby Mershal形式で保存される。 ここには本文は含まれないが、大概メタ情報にアクセスしたい場合と本文にアクセスしたい場合は別なので、分けている。

PureBuilder Simplyにおいて、「メタ情報を文書に書き、処理した情報はデータベースに書いておく」というのは設定上の核であると行って過言ではない。 これにより、文書のメタ情報を扱うことはPureBuilder Simplyの外で行うことができるのだ。

タグページは.tagcloud.rbというスクリプトによって生成しているが、 これはMarkdownページを生成する。つまり、「タグページ自体をPureBuilder Simplyによって生成すべきページとして生成する」のである。 タグを含むページを生成・更新した場合は再度タグページを生成し直すことになり、そのためにrefreshというスクリプトもある。この場合、文書ページではないタグのindexを処理されてしまうと困るので次のような処理になっている。

では.tagcloud.rbは、というとこちらも結構単純。

見ての通り著しい力技でデータベースを集計し、最終的にはMarkdownを出力している。

従来もほとんどこうだったのだが、ページ側でエスケープしていないという理由でエスケープしていなかったので追加した。

おわりに

もう少し難しいかと思っていたのだが、どうやらPureBuilder Simplyの設計が思っていた以上に優れていたようで、タグ機能もスムーズに実装することができた。

PureBuilder Simplyは傑作といって差し支えない出来になっている。 もともと思っていたよりもずっと優れたツールになっているのだ。

PureBuilder SimplyはPureBuilderとしては実に3作目である。 Zshの機能をフル活用したPureBuilder, Windowsでも動作可能なようRubyで書かれたPureBuilder2。ページの生成にはいずれも活用できたが、サイト構築労力が高く、安価な案件で利用するにはしんどいものがあった。 また、構築できる内容も割と画一的だったため、様々な要求に応えるのは難しかった。 Zshで書かれたPureBuilderは構築時に任意のZshスクリプトを実行できる方式だったため、なんでもできるといえばできるのだが、サイト構築がプログラミング色の強いものになっていた。これはちょっとユーザーフレンドリーではない。

PureBuilder Simplyは名前の通りずっとシンプルだが、いままでよりずっと強力になった。

小さなスクリプトを書くことは、多くのプログラマにとってはあまり馴染みのないことかもしれないが3、やろうと思えば発想さえ知れば決して難しいことではないはずだ。


  1. これはReSTでもdocutilが許容しないような規格化されていないメタ情報を書くということだ。

  2. ちなみに、Chienomiではタグはすべて英語になっている

  3. Unixに浸っている人はむしろ息をするようにのように行動するだろう。PureBuilder Simplyの考え方はこれに基づいている。

Google Chromeが7月のversion 69でSSLを要求

どうなるのか

Google Chromeが7月にSSL(HTTPS)でないサイトに対して警告するようになる、という話を聞いたので、現在Developer Buildのversion 69をインストールして試してみた。

内容はIT Mediaの記事が正しかった。 つまり、

  • 従来は「HTTPSサイトは表示」だったものを「HTTPサイトは表示」に変更
  • version 70からはHTTPサイトでのフォーム入力時は警告表示

日経BPの記事をみると、まるでSSL署名が正しくないときのような警告がなされるかのように読み取れるがそんなことはない。

話はこういうことだ。

もともとはHTTPが標準だった。HTTPSであることは特別であり、故に「保護されています」と表示していたわけだ。 しかし、今後はHTTPSが標準であるため、いちいちHTTPSであることは強調せず、HTTPであることは逆にリスクがあることを示す、というわけだ。

この変更は正しいと思う。 対応した環境でブラウジングしているのに、HTTPではなくHTTPSであることで困る、ということは考えにくく、HTTPSであることに対してわざわざ注意を引く必要はない。 むしろHTTPであることはリスキーな状態であり、注意を示しておくべき状況だ。 特にフォーム入力を伴うときは警告すべき状況だといっていい。

状況 現在 version 69 version 70
HTTP 表示なし 「保護されていません」(グレー) 「保護されていません」(グレー)
HTTP + フォーム入力 表示なし 「保護されていません」(グレー) 「保護されていません」(赤)
HTTPS 「保護されています」(緑) 鍵マーク(グレー) 鍵マーク(グレー)

極めて正しいように見える。

GoogleがLet’s Encryptを推進したこととも整合性がとれている。

ただ、それなら「スキームなしでアドレスを入力したときにHTTPでアクセスする」ということはやめるべきだし、 むしろ「http://と指定した場合でもHTTPSを試行する」べきではないのか。

どうすべきなのか

これからは明確にHTTPSが標準となっていき、現在HTTPSに対応していないウェブサイトは「リスキーである」という認知が進むだろう。 これは、事情がわからないユーザーがHTTPの「安全でないサイト」を避ける可能性があるということを含んでいる。

  • HTTPSに非対応はもはや論外。早急に対応すること
  • HTTPSトライはしてくれないので、レガシーデバイスを切り捨てるならHTTPアクセスは301を返すべき
  • HTTPS強制にするにしても、HTTPアクセスを許容するにしても、ヴィジターへの説明はしたほうがよさそう

一応、Mimir YokohamaではHTTPS強制で、HTTPはサブドメインで許容する予定。