Chienomi

Mimir Yokohamaのウェブサイトのリニューアル 〜 究極のUXとアクセシビリティを求めて

開発::web

  • TOP
  • Articles
  • 開発
  • Mimir Yokohamaのウェブサイトのリニューアル 〜 究極のUXとアクセシビリティを求めて

今回、Mimir Yokohamaのウェブサイトのデザインリニューアルを行った。

テンプレートの書き直しとCSSの変更が主であるデザイン変更であり、全体的なデザインテイストは変わっていない。

変わったのはレイアウトとナビゲーションである。

より読みやすく

どのような変更をしたかについては記事としてあるが、もう少し踏み込んだ内容をこちらでしよう。

基本的にはその記事にある通り、ここまでのサイトの使用感と感想を元にリデザインを行った。 好評なのはこのChienomiのデザインである。

Chienomiは情報量の多いサイドカラムを立ててスクロールを独立させており、(あまり有用ではない)サイドカラム内ナビゲーションを用意している。 また、コードブロックなどが頻出するため本文幅はナリとなっており、ウィンドウサイズの調整で好みに合わせられる。

Chienomiをスマートフォンで見る人は比較的少ない(それでも4割ほどはスマートフォンだが)であることを踏まえて、スマートフォン向けのナビゲーションはナビゲーション部分へのジャンプとなる「TOC」ボタンだけとなっている。

基本的には「リーダービューのような読みやすさ」を意識しており、本文にできるだけ表示領域を取るよう工夫している。

この読みやすさに対する意識を継承しているのだが、これは同時に「正しいとされているレイアウトが読みづらい」という私の主張の表現でもある。

Mimir Yokohamaが採用していた旧WordPress流デザインの問題点は、

  • ナビゲーションへのアクセスがしづらい (長い本文のスクロールに連動してしまうから)
  • 長い本文だと画面が左側に寄った状態になる
  • 論理1000pxくらいの環境だと表示領域が少ない
  • ハンバーガーメニューのことをみんな知らない

であった。

PC版デザイン

左側にラベルつきアイコンが並ぶデザインは、ウェブサイトではあまり見ないものだが、Windowsアプリでは割とメジャー「だった」デザインである。 (Windows XP世代のアプリではよく見かけたが、最近はあまり見ない)

単純にvisibleなページ上に全てのドキュメント要素を配置し、それに対するページ内リンクでアクセスできるようにした。

ページ内リンクというのは昔からあるものではあるが、実のところあまり活用されてこなかった。 基本的にはウェブデザインというのは「フレームからJavaScriptへ」という感じだったが、この中ではどちらでもあまり活用されない。

Wikiではページ内リンクは中核的なアクセス方法だが、それ以外ではせいぜい検索エンジン向けであり、「あるだけ」でそんなに積極活用されていないわけだ。 もしかしたらページ内リンクのことを知らない人もいるかもしれない。

一方で、エレメント同士を離して配置し、ページ内リンクによって変遷なしに表示させる、というテクニックも2005年頃からよく使われていた。 結局のところ、どのようなUIであれ目的としては「希望する要素を画面上に表示させる」ことである。

ページ内リンクははるか昔からあるだけあって、非常に安定して動作する。 なによりも、ページ内にvisibleな要素として配置しておくことで、例えナビゲーションが機能しないケースにおいてもその要素にアクセスできず、結果ページが機能しないというような事態を避けることができる。

だから、今回の基本的な方針は

  • プライマリなナビゲーションエレメントは画面上に全て表示する
  • セカンダリなナビゲーションエレメントはアクセスできなくても支障はないものとする
  • セカンダリなナビゲーションエレメントはプライマリなナビゲーションエレメントに対するショートカットとして機能させ、画面上に常に表示させる

である。

これはChienomiでもそうだが、ナビゲーションエレメントはウィンドウにフィットさせたfixedで配置することになる。

だが、Chienomiと違ってビギナーをターゲットとするMimir Yokohamaはウィンドウサイズの変更、タイリング、タブスタックタイリングといった手法をユーザーが思いつかない可能性が高い。また、幅をフルに使いたい理由も乏しいのでMimir Yokohamaでは最大幅を1380pxに制限している。 このことから、同じfixed配置とはいえその配置の仕方という点では異なる。

セカンダリなナビゲーションエレメントの中にTOCがあり、二段跳びすることでドキュメント内のより詳細な位置に飛べるため、文書内の実質あらゆる位置へのショートカットが用意されている上に、プライマリなナビゲーションエレメントがサイトの主要なドキュメント(あるいはそのリンク群)へのナビゲーションになるため、ステップ数が非常に少ない。

また、単純にJavaScriptで処理するよりも「速い」という点も大きい。 割とtype aheadな私はアニメーション処理されたりすると意図した動作をしてくれないことが多々あり、かなりストレスになっている。 それと比べ、ページ内リンクでaheadすることなどまず不可能なので良好なレスポンスで(戸惑うかもしれないが)気持ちいい。

このことから、ナビゲーション構造としては非常に良いと考えているわけだ。

今回、レイアウト切り替えの閾値が変わって、991pxになった。 これは10.6inchのLet’s Note SZの最大化ウィンドウでサイドバーつき(要はVivaldi)でも割り込まない値であり、なおかつFullHDだとタイル時にボーダーレスで割り込む値である。本当は971pxあたりのほうが良いかもしれない。

これは、従来は「デスクトップのタイル時にワイドレイアウトを維持する」という考え方をしていたのだが、やはりちょっと狭く、今回は逆に「タイルならナローレイアウト」という考え方に振った。 2550px3840pxある場合はいくらなんでもタイルしても大丈夫だろう。 (それでも避けたいのであればビューポートの向きで判定することはできる)

スマートフォン版デザイン

スマートフォン版デザインにおいて重視しているのは、「本文を読んでいるときに本文以外の要素に画面を割かない」ということである。

古いAndroid端末を使っている人などは試してもらうと良いと思うのだが、昔のAndroidブラウザなどはナビゲーションを上下に表示しており、画面の縦幅がとても狭い。 4.5インチ16:9とかだったにも関わらずだ。 一方、最新のFirefox FocusやVivaldi mobileは閲読中、ナビゲーション要素は消えるので、画面全体でウェブページが見られるようになっている。

つまり、スマートフォンにおけるウェブ閲覧というのは「画面上に他の要素を表示しない」という方向に進化してきたのだということがわかる。 その流れに則れば、スマートフォン版デザインにおいては「本文閲読中、本文が画面全体を使うようにすべき」ということになる。

最近私のサイトはこの流れを尊重しており、Chienomiでも「TOCボタン」だけが残るデザインだ。 Mimirもほぼこれになった。

Mimirの場合はもう少し親切なナビゲーションが必要ということで、Back to TOPボタンが設置され、トップに各種ナビゲーションへのナビゲーションが配置される、という二段跳び方式になった。 アイコンはバナーのサイドにコンパクトに配置される。未反映だが最新の開発版では記事タイトルも縮小されており、本文以外の要素をより縮めるように工夫している。 これはページ内リンクが使われ、非常に安定して動作するが、JavaScriptがこれをオーバーライドする形でナビゲーション用のモーダルウィンドウが表示される。

結局2タップであることには変わりないのだが、説明がなく、小さなアイコンが並ぶ標準のものよりも、わかりやすくタップしやすい。

アイコンを並べるために幅が溢れてしまわないよう、ビューポート幅に対しては細かな調整がなされている。

JavaScriptによるナビゲーションは少し見どころが多い。

イベント対象エレメントはa要素を内包しており、イベントリスナが呼ばれない場合ページ内リンクとして機能する。 一方、clickイベントリスナはEvent#stopPropagation()Event#preventDefault()を呼ぶため、イベントリスナが正常に機能するならばページ内リンクは機能せず、セカンダリなナビゲーションエレメントが表示される。

セカンダリなナビゲーションエレメントを表示するのに使われている要素は PC版と同一である

PC版では常に表示され、モバイル版ではモーダルウィンドウになるエレメントだが、それ自体はJavaScriptではなくCSSによって切り替えられている。 JavaScriptで直接表示を制御しない理由は、ウィンドウリサイズ時の処理がどうしても環境によっては抜け穴ができておかしなことになってしまうからだ。

では実際にこれをどう切り分けているのか、というと、「上位要素のクラス」である。

実際にCSSを見てもらった方が早いかもしれない。

@media screen and (max-width: 990px) {
  .spnav_closed #QuickNav {
    display: none;
  }
  .spnav_open #QuickNav {
    background: #000000fa;
    position: fixed;
    width: 100%;
    height: 100%;
    max-width: 100%;
    max-height: 100%;
    overflow: auto;
    text-align: center;
    z-index: 900;
    display: block;
  }
  /* ... */
}

@media screen and (min-width: 991px){
/* ... */
  #QuickNav {
    display: block;
    height: 100%;
    max-height: 100%;
    width: 98px;
    max-width: 98px;
    border-right: 2px solid #606060;
    position: fixed;
    left: auto;
    overflow: auto;
    text-align: center;
  }
/* ... */
}

つまり、

  • ナロー用メディアクエリが適用される場合、上位要素のクラスによって表示・非表示を変更する。表示する場合は全体を覆うモーダルウィンドウ
  • ワイド用メディアクエリが適用される場合、上位要素のクラスは一切参照せず、常にサイドバーとして表示する

ということだ。そのため、JavaScriptは単に上位要素のクラスを変更するだけで、現在の状態(モーダルウィンドウの表示/非表示と、ビューポート幅)は一切気にする必要がない。

そして、JavaScriptがやっているのは本当にクラスの切り替え だけ である。 以下がJavaScriptの全容だが、

(function() {
  var overall = document.getElementById("TrueOverall")
  var nav = document.getElementById("QuickNav")
  
  document.getElementById("BackToTop").addEventListener("click", function(event) {
    overall.className = overall.className.replace(/spnav_\w+/, "spnav_open")
    event.stopPropagation()
    event.preventDefault()
  })

  nav.addEventListener("click", function(event) {
    overall.className = overall.className.replace(/spnav_\w+/, "spnav_close")
    event.stopPropagation();
  })
})()

と極めて単純だ。

そのポイントは「クローズ時にEvent.preventDefault()しない」ということである。

イベントリスナは例えイベントを発生させたデフォルトの動作を持つ要素よりも上位要素に設定されていたとしても、デフォルトの動作より 先に 呼ばれる。 ここで呼ばれたイベントリスナによってイベントを発生する要素がdisplay: noneとなり、消える。 もちろん、display: noneな要素はイベントを発生しない。イベントが伝播されるのはイベントリスナによって要素が非表示になった後だ。

ところが、イベントの伝播は「イベントが発生した要素に対して」行われる。 イベントリスナがdisplay: noneにしたとしてもそのイベントの伝播に関してはイベント発生時の状態が対象なのである。 そのため、 clickイベントにより発生したイベントで呼ばれたイベントリスナによって、そのイベントを発生させた要素そのものが非表示になったとしても、非表示にされたa要素に対するclickの伝播は行われる のである。

そして、前面のブロック要素が覆っている場合、裏にある要素に対してイベントは伝播しないので、本文中のリンクをクリックしてしまうようなことはない。 これによって、モーダルウィンドウの消去とページ内リンクのクリックの処理を非常にシンプルな考えで同時に行うことができる。

ちなみに、Event#stopPropagation()はしている理由は、(実際にはないはずだが)グロッサリーツールチップやライトボックスといった他のモーダルウィンドウと同時に表示されていた場合に、他のモーダルウィンドウを消してしまわないためである。

Fallback デザイン

一般的には最新ブラウザ環境とIEのことだけを考えるものだが、私は「いかなる環境でもユーザーを追い出さない」を掲げているので、想定と異なる環境でも利用可能であることは重要視している。

なので、HTML, CSS, JavaScriptいずれを見てもややレガシーというか、ダサい(モダンではない)書き方をしているのに気づくこともあるだろう。また、「必要ない」と思えるような書き方をしている箇所も見つかるだろう。 それも、単に旧規格で書いているわけではなく、新しい機能も入っているのだが、それにしてはややこしい。

これは、かなり厳しい制約の上で書いているために、機能上の問題だ。 単に最新ブラウザだけで見せるのであればもっと違う書き方をする。

実際のところ、私が書くサイト、特にMimirに対してはかなり複雑な考え方をしている。

これは高度なアクセシビリティのためだ。 Mimirのウェブサイトは盲目, 弱視, 識字困難, 色盲, 低速回線, レガシー環境, マイナーウェブブラウザ, コンソール環境などを想定して表示チェックしている。

その中で重視しているのは、「どのような環境であっても利用不可能、あるいは正常に識別できない状態にしない」である。 (厳密に言うと、漢字が読めない、日本語が苦手といったケースは除外している。だが、これは文章上の問題であり、デザイン上の問題ではない)

まず重要なのは、「構造的に単純にHTMLパースするだけで読める文書が生成できる」ことである。 これはCSSもJavaScriptもなしに、HTML要素をごく基本的なレベルで解析して抜き出すことを意味している。 最悪、単なるタグ除去も許す。

これができると、まず非常にマイナーな環境(ユーザースクリプトなどを含む)で対処可能になる。

次に、可視性を考慮したデザインである。

CSSから可視性情報だけを拾うブラウザというのはとても多い。 音声ブラウザなども含め、display: noneはまず間違いなく認識する。

また、こうした「CSSを厳密に解釈しようとはしない」ブラウザの場合、CSS1にあるごく基本的なセレクタ、つまり

  • tag
  • .class
  • #id
  • sel sel
  • sel > sel

は認識する、という場合が多い。 一方、メディアクエリは一般的に無視される。 (これは、メディアクエリつきで書かれているもの全体を無視するのが一般的である)

これを踏まえて、この条件で解釈可能なものと、HTML要素そのものの可視性を組み合わせたときに必要な情報 のみが 表示されるようにする。

なぜ「のみ」なのかというと、このように解釈するものがヴィジュアルブラウザであるという保証がないからだ。 音声ブラウザや点字ブラウザなども一般的にこのように解釈するし、ウェブスパイダーもそのように解釈するものが結構ある。

そのことから、画面上のオーバーレイ要素、ナビゲーションボタン、装飾などはゴミになったり、レイアウト崩れにつながる。 だから、ショートカットなどの便利機能一切を捨て、本質的に必要な「本文+メニュー」の構成になるようにする。

このようにすることで、Dilloやw3mでも正常に表示できるページが出来上がる。

そして古典的なウェブブラウザの解釈に合わせる。 これは、IE6やMozilla 1.0を想定した場合に、致命的な表示にならないようにするのである。 解釈違いによってデザインが希望通りにならないのは仕方ないが、ある環境で閲読不可能になる、という状況を回避するようにするのである。

また、JavaScriptに関しては「なくても構わない」ようにする。 これは、 JavaScriptは全く機能しないか、完全に機能するかのどちらかにする というのもあるし、例えどこでエラーになる状況であったとしても問題を起こさないようにする、ということでもある。

例えば、モバイル版表示ではボタンクリックによってナビゲーションを表示するようにしているが、これはEvent#stopPropagation()Event#preventDefault()によって抑え込んでいるだけであり、JavaScriptがなければページ内リンクとして機能し、ページ先頭に跳び、ページ先頭のナビゲーションボタンが利用できる。 マイナーな環境では保証できないが、レガシーな環境ではイベントリスナ中でのエラーはそのイベントリスナを無視してイベントを伝播するので、機能が死んでも別に困りはしない。さらにいえば、そもそもイベントリスナ自体がElement#addEventListenerによって追加されていることで、本当にレガシーな環境ではイベント処理自体が発生しないようになっている。 (実際はそれ以前に、deferつきでhead内で読んでいるので、HTML的に比較的新しいブラウザでないと機能しない)

こうした何段構えものアクセシビリティ確保によって「閲読不可能」を避ける徹底がなされている。

なお、印刷時はナビゲーション要素一切が必要ないことから、印刷時に関してはさらに特別な扱いをしている。

フォントとフォントサイズ

基本コンセプトとしては「見出しはゴシック体・本文は明朝体」という旧来からの印刷物のスタイルを継承している。

また、Mimirのユーザーはブラウザでフォントの設定をしていない可能性が高いので、こちらで明示的に指定する方向性は変わっていない。ただし、見出しに関しては「丸ゴシックを使いたい」という意図で指定していたのだが、それは非本質的なのでsans-serifにした。

本文フォントの指定の仕方は大きく変わった。 そして、非常に変わったものになった。

font-family: 
/* Latin */
"Fira Sans", "Roboto", "Ubuntu", "Avenir", "Segoe",
/* Windows/Mac Mincho fonts */
"BIZ UDPMincho", "Yu Mincho", "YuMincho", serif,
/* FontConfig fallback */
"Noto Serif CJK JP", "Source Han Serif JP", "IPAPMincho", "Sawarabi Mincho", "Kochi Mincho", "Sazanami Mincho";

基本的な考え方は次の通りである。

  1. 日本人としては読みやすい欧文のヒューマニストサンセリフなので、ヒューマニストサンセリフの指定
  2. WindowsとMacに関してはモダンな明朝体の指定
  3. serif
  4. FontConfig環境では(Windowsでも?) serifに欧文書体が指定されているとデフォルトフォント(恐らくサンセリフ系)にフォールバックしてしまうので明示的要求

最後のがわからない人も多いかもしれない。

Chromium/Firefoxブラウザはフォントを要求するとき、複数のフォントがそのフォント名に対する候補として存在するとしても第一のフォントしか使わない。 だから、serifに欧文フォントと和文フォントをリストすることで、シンプルに和文フォントの欧文を欧文フォントに置き換えるということが効くのだが、このような環境で、なおかつウェブブラウザでセリフ体フォントにserifを指定すると、serif要求時に欧文フォントだけが取られ、和文フォントが拾えない。 こうなると、「選択可能なフォントがない」という状態になり、デフォルトフォントが使われるようになる。 一般的にデフォルトフォントはsans-serif系列のフォントを使うので、そうなる前にできるだけ拾えるようにしたわけだ。

長文読みには明朝体が適しているが、「明朝体は線が細くて見づらい」という声がある。 実のところこれはかなり最近になって出てきたもので、なぜならばMS明朝は一般的なサイズにおいてビットマップフォントであり、MS明朝とMSゴシック共に線幅は1pxだったからだ。

だが、現状においてこの主張はあまり間違っているとは言い難い。 実際、明朝体は細めに見える。

そこで一度は

font-weight: 500

を試したのだが(この指定ではMediumがあればMediumが適用され、そうでなければRegular/Normalが適用される)、源ノ明朝でうまく動作しない上に、欧文書体ではうまく動作してしまうため、さらにウェイト差が広がって見えてしまうことから断念し、背景色をよりウォームに調整した。これにより、背景の白色度が下がって膨張度が抑えられ、見づらさが緩和されている。

正直、Mimir Yokohamaって白くてクリーンなイメージなのではっきりわかるほどウォームにすることにはちょっと抵抗があるのだけど、見やすさ優先。 ここらへんは今後調整していくかもしれない。

また、最近私の中では「フォント大きめ」がアツイ。 別に老化で小さい字が見づらくなったという話ではなくて、常用漢字を表示するために少なくとも縦28pxが必要である、ということを踏まえて、「そもそも(サブピクセルレンダリングに関係なく)10pxとか12pxが漢字を読めるという発想が間違っている」という考えに至ったのだ。

まぁ、言うならば4kディスプレイを買ってからフォントスケーリング1.1にして、フォントサイズも大きめ(これを書いている私のVSCode環境だと18px。なので20px描画)にしているのだが、必然的にフルHDのほうにウィンドウを持っていくと文字は大きい。だが、文字が大きくて表示量が少ないとしても、大きめは確実に読みやすい、という結論に至った。

そこで、「表示領域が余ってるんやったら、大きくしよや」という発想に至る。 スマートフォンの場合、解像度が高く、スケールも大きいためあまり大きくする必然性がない。 PCでナロー表示ならズームすれば良いのでやはりあまり必然性がない。

だが、デスクトップ表示では今回最低限必要とするビューポート幅が増えたこともあり、115%に設定され、さらに1210pxを超過する場合は125%に設定されている。 これによってより見やすく、読みやすくなっているはずだ。

TOC

HTML上はMimir YokohamaのウェブサイトではTOCは3箇所にあった。

  • ハンバーガーメニュー内
  • 記事先頭
  • サイドカラム内

これは、Mimir Yokohamaのアクセシビリティと実際のユーザーのアクションとの間にある問題を如実に表した問題であった。

当初、ハンバーガーメニュー内にのみ配置されていたTOCだが、実際にはハンバーガーメニューにアクセスする人がいない。 そこで、サイドカラムに配置したのだが、サイドカラムはナローウィンドウモードでは下に行ってしまうので、ナローウィンドウモードにおいては記事先頭に出すようになった。

このような理由で旧デザインにおいてはテンプレートシステムを使って同一内容を複数箇所に配置しているものが多く、「必要のない変な反復」だと私としては大きな不満があった。 ドキュメントとしてもクリーンじゃない。

今回、ページ内ナビゲーションを使うという方針にしたことで、これを一箇所にまとめることができた。

Unicodeナビゲーションアイコン

今回追加されたナビゲーション機能はアイコンにUnicodeのemojiを使っている。

ちなみに、U+FE0F(VARIATION SELECTOR-16)入り。 これでできるだけよりアイコンらしい絵文字を入れるようにしている。 入れ方に関しては、FcitxでShift+Ctrl+Alt+u(Fcitx Unicode Typing Support)。

画像のほうが表示可能性は高い気がするけど、せっかく絵文字が充実していることだし、アクセシビリティ的にも軽量化的にもUnicode使おうという方向になった。 ラベルにテキストを入れることでアクセシビリティにさらなる配慮もしている。